If I create a custom template or class that is supposed to implement a concept defined by a set of traits. How can I test that it actually does implement that concept?
Is there a generally accepted way of doing this for 'traits' classes? (concepts in C++20 are the accepted way in general but I am currently limited to C++14-ish)
Or is it just a case of having to remember?
Some related examples:
If I write a fancy pointer that I want to be sure implements pointer_traits. How can I check that it actually does?
If I write a custom allocator that I want to be sure implements allocator_traits. How can I check that it actually does?
How can I check I've properly implemented iterator_traits?
For example if I implement a 'fancy' pointer along the lines of boost::interprocess::offset_ptr:
I do the normal things:
- implement & test constructors from T* T&
- implement & test comparisons <=, <, !=, ==, >=, >=
- implement & test dereferencing operator* & operator->
- implement & test assignment
- implement & test pointer arithmetic ++, --, -, +, +=, -=
To use this type with an allocator we need to implement pointer traits. Is it just a case of implementing to_address() and pointer_to()?
You can make a pointer-like class which does not implement comparisons or pointer arithmetic. Would that still be a valid model of pointer_traits? This question suggests that a valid pointer must implement the random access iterator concept. So the answer would seem to be no. That question is also close to what I'm trying to do.
I expect there are two answers to this. A concepts version and one for earlier iterations of C++. I am interested in both though I am currently limited to C++14.
There is the obvious answer which is to add a test which uses the thing you want to use. So we add an allocator which uses the pointer and add a test using that allocator with an STL type. This is useful and necessary but not necessarily complete or sufficient. It is also relatively speaking an integration test (does X work with Y) rather than a unit test (does X do what X should do).
It is very easy to miss something. For example when implementing smart pointer comparisons it is easy to forget to make the following valid.
nullptr == smartPtr
Likewise I suspect there are some things that will work if some of the traits are implemented and others that won't if some a not implemented correctly. Hence, I want to add a test or tests to the pointer class's testsuite to ensure it implements everything it needs to implemented to be used with the allocator template.
I also want to better understand what allocator traits, pointer traits and iterator traits are really doing.
Related reading:
- http://blog.nuggetwheat.org/index.php/2015/09/01/why-pointer_traits-was-introduced-in-c11/ and links.
- https://github.com/Quuxplusone/from-scratch/blob/master/include/scratch/bits/traits-classes/pointer-traits.md
Update 1
Speaking of missing things. I already realised I need:
- explicit operator bool () const;
- bool operator! () const;
- rebind
- using iterator_category = std::random_access_iterator_tag;
Found by attempting to instantiate boost::containers::vector and std::vector. There are some others I am still trying to sort out along with the rebind part.