Library: finish new form of the type rebinding trait

This commit is contained in:
Fischlurch 2017-11-30 23:37:11 +01:00
parent 1047f2f245
commit 674201f5ea
2 changed files with 121 additions and 91 deletions

View file

@ -1,5 +1,5 @@
/* /*
VALUE-TYPE-BINDING.hpp - control type variations for IterAdapter VALUE-TYPE-BINDING.hpp - control type variations for custom containers
Copyright (C) Lumiera.org Copyright (C) Lumiera.org
2010, Hermann Vosseler <Ichthyostega@web.de> 2010, Hermann Vosseler <Ichthyostega@web.de>
@ -21,14 +21,31 @@
*/ */
/** @file value-type-binding.hpp /** @file value-type-binding.hpp
** Type re-binding helper template for IterAdapter and friends. ** Type re-binding helper template for custom containers and adapters.
** This header defines a trait template which is used by the Iterator ** This header defines a trait template which is used by the Iterator
** adapters to figure out the value-, pointer- and reference types ** adapters and similar custom containers to figure out the value-,
** when wrapping iterators or containers. For extending the use of ** pointer- and reference types when wrapping iterators or containers.
** Iterator adapters and Iter-tools to situations involving const **
** iterators or custom containers, explicit specialisations ** When writing a generic container or adapter, there is typically some point
** might be injected prior to instantiating the Iterator adapter ** where you'd need some variation of the payload type: you may want to expose
** template. ** a reference, or you might need a pointer to the type, to implement a forwarding
** `operator->()`. On a technical level, this turns out surprisingly tricky, since
** we often don't know the exact "incantation" of the payload type and might thus
** end up forming a pointer to a rvalue reference or likewise illegal constructs.
**
** Within the STL, there is a convention to provide nested typedefs to indicate
** type variations in relation to the basic payload type of the container. We
** follow this convention and support especially the
** - `value_type`
** - a simple (LValue) reference to the payload
** - a pointer at the payload.
**
** A custom container should likewise provide such type definitions, and the
** type rebinding helper template defined in this header makes it easy to
** provide such nested type definitions in a flexible way. This usage also
** creates an *Extension Point*: when some payload type requires special
** treatment, an explicit specialisation to this rebinding trait may be
** injected alongside with the definition of the payload type.
** **
** @see iter-adapter.hpp ** @see iter-adapter.hpp
** @see scope-path.hpp usage example (explicit specialisation) ** @see scope-path.hpp usage example (explicit specialisation)
@ -40,8 +57,7 @@
#include "lib/error.hpp" #include "lib/error.hpp"
//#include "lib/meta/duck-detector.hpp" ////////TODO #include "lib/meta/util.hpp"
#include "lib/meta/util.hpp" ////////TODO
@ -49,7 +65,7 @@
namespace lib { namespace lib {
namespace meta { namespace meta {
namespace { namespace { // Helper trait to detect nested value_type binding definitions
template<typename TY> template<typename TY>
class has_nested_ValueTypeBindings class has_nested_ValueTypeBindings
@ -71,6 +87,10 @@ namespace meta {
}; };
} }
/**
* @internal helper template to pick up nested value type definitions
*/
template<typename TY, typename SEL =void> template<typename TY, typename SEL =void>
struct ValueTypeBinding struct ValueTypeBinding
{ {
@ -79,10 +99,7 @@ namespace meta {
typedef TY* pointer; typedef TY* pointer;
}; };
/** /** specialisation for classes providing STL style type binding definitions */
* specialisation for classes providing
* STL style type binding definitions themselves
*/
template<typename TY> template<typename TY>
struct ValueTypeBinding<TY, enable_if<has_nested_ValueTypeBindings<TY>> > struct ValueTypeBinding<TY, enable_if<has_nested_ValueTypeBindings<TY>> >
{ {
@ -92,13 +109,17 @@ namespace meta {
}; };
/** /**
* Type re-binding helper template for creating nested typedefs * Type re-binding helper template for creating nested typedefs
* for use by IterAdapter or similar "Lumiera Forward Iterators". * for use by custom containers and iterator adapters or similar.
* This trait provides a value-, reference- and pointer type, * - this trait provides a value-, reference- and pointer type,
* similar to what the STL does. * similar to what the STL does.
* - references are stripped, otherwise the base type is passed through
* - _but_ when the base type in turn provides such nested typedefs,
* they are picked up and retrieved as result.
* @note client code might define specialisations * @note client code might define specialisations
* to handle tricky situations (like const_reverse_iter) * to handle tricky situations (like e.g. const_reverse_iter)
*/ */
template<typename TY> template<typename TY>
struct TypeBinding struct TypeBinding

View file

@ -1,5 +1,5 @@
/* /*
ValueTypeBinding(Test) - human readable simplified display of C++ types ValueTypeBinding(Test) - verify handling of value_type definitions on custom containers
Copyright (C) Lumiera.org Copyright (C) Lumiera.org
2017, Hermann Vosseler <Ichthyostega@web.de> 2017, Hermann Vosseler <Ichthyostega@web.de>
@ -47,7 +47,7 @@ namespace test{
struct Outer struct Outer
{ {
struct Inner struct Inner
{ {
T val; T val;
}; };
@ -57,7 +57,6 @@ namespace test{
}; };
struct Space { }; struct Space { };
using Join = ulong;
template<typename X> template<typename X>
struct Bugg struct Bugg
@ -161,7 +160,7 @@ namespace test{
* to indicate those types. The meta::TypeBinding helper allows to pick up such * to indicate those types. The meta::TypeBinding helper allows to pick up such
* definitions, and additionally it levels and unifies access for various combinations * definitions, and additionally it levels and unifies access for various combinations
* of primitive types, references and pointers. The purpose of this test is to verify * of primitive types, references and pointers. The purpose of this test is to verify
* and document this behaviour. * and document this behaviour.
* *
* @see value-type-binding.hpp * @see value-type-binding.hpp
* @see lib::RangeIter * @see lib::RangeIter
@ -173,94 +172,104 @@ namespace test{
void void
run (Arg) run (Arg)
{ {
// verify the type diagnostics helper...
CHECK ("int" == showType<int >() );
CHECK ("int&" == showType<int& >() );
CHECK ("int &&" == showType<int&& >() );
CHECK ("int const&" == showType<int const& >() );
CHECK ("const int &&" == showType<int const&& >() );
CHECK ("int *" == showType<int * >() );
CHECK ("const int *" == showType<int const * >() );
CHECK ("const int * const" == showType<int const * const >() );
CHECK ("int const*&" == showType<int const * &>() );
CHECK ("int const* const&" == showType<int const * const&>() );
// Test fixture: the template Outer<T> provides nested value type bindings
using OuterSpace = Outer<Space>; using OuterSpace = Outer<Space>;
using Join = ulong;
cout << showType<OuterSpace::value_type>() <<endl; CHECK ("Space" == showType<OuterSpace::value_type>() );
cout << showType<OuterSpace::reference>() <<endl; CHECK ("Outer<Space>::Inner&" == showType<OuterSpace::reference>() );
cout << showType<OuterSpace::pointer>() <<endl; CHECK ("shared_ptr<Space>" == showType<OuterSpace::pointer>() );
cout << showType<TypeBinding<OuterSpace>::value_type>() <<endl; // ...such nested type bindings will be picked up
cout << showType<TypeBinding<OuterSpace>::reference>() <<endl; CHECK ("Space" == showType<TypeBinding<OuterSpace>::value_type>() );
cout << showType<TypeBinding<OuterSpace>::pointer>() <<endl; CHECK ("Outer<Space>::Inner&" == showType<TypeBinding<OuterSpace>::reference>() );
CHECK ("shared_ptr<Space>" == showType<TypeBinding<OuterSpace>::pointer>() );
cout << showType<TypeBinding<Outer<Join>>::value_type>() <<endl; CHECK ("unsigned long" == showType<TypeBinding<Outer<Join>>::value_type>() );
cout << showType<TypeBinding<Outer<Join>>::reference>() <<endl; CHECK ("Outer<unsigned long>::Inner&" == showType<TypeBinding<Outer<Join>>::reference>() );
cout << showType<TypeBinding<Outer<Join>>::pointer>() <<endl; CHECK ("shared_ptr<unsigned long>" == showType<TypeBinding<Outer<Join>>::pointer>() );
cout << showType<TypeBinding<Space>::value_type>() <<endl; // contrast this to a type without such nested bindings
cout << showType<TypeBinding<Space>::reference>() <<endl; CHECK ("Space" == showType<TypeBinding<Space>::value_type>() );
cout << showType<TypeBinding<Space>::pointer>() <<endl; CHECK ("Space&" == showType<TypeBinding<Space>::reference>() );
CHECK ("Space *" == showType<TypeBinding<Space>::pointer>() );
cout << showType<TypeBinding<OuterSpace&>::value_type>() <<endl; // reference types will be levelled (reference stripped)
cout << showType<TypeBinding<OuterSpace&>::reference>() <<endl; CHECK ("Space" == showType<TypeBinding<OuterSpace&>::value_type>() );
cout << showType<TypeBinding<OuterSpace&>::pointer>() <<endl; CHECK ("Outer<Space>::Inner&" == showType<TypeBinding<OuterSpace&>::reference>() );
CHECK ("shared_ptr<Space>" == showType<TypeBinding<OuterSpace&>::pointer>() );
cout << showType<TypeBinding<OuterSpace&&>::value_type>() <<endl; CHECK ("Space" == showType<TypeBinding<OuterSpace&&>::value_type>() );
cout << showType<TypeBinding<OuterSpace&&>::reference>() <<endl; CHECK ("Outer<Space>::Inner&" == showType<TypeBinding<OuterSpace&&>::reference>() );
cout << showType<TypeBinding<OuterSpace&&>::pointer>() <<endl; CHECK ("shared_ptr<Space>" == showType<TypeBinding<OuterSpace&&>::pointer>() );
cout << showType<TypeBinding<OuterSpace const&>::value_type>() <<endl; CHECK ("Space" == showType<TypeBinding<OuterSpace const&>::value_type>() );
cout << showType<TypeBinding<OuterSpace const&>::reference>() <<endl; CHECK ("Outer<Space>::Inner&" == showType<TypeBinding<OuterSpace const&>::reference>() );
cout << showType<TypeBinding<OuterSpace const&>::pointer>() <<endl; CHECK ("shared_ptr<Space>" == showType<TypeBinding<OuterSpace const&>::pointer>() );
cout << showType<TypeBinding<OuterSpace*>::value_type>() <<endl; // but a pointer counts as a different, primitive type. No magic here
cout << showType<TypeBinding<OuterSpace*>::reference>() <<endl; CHECK ("Outer<Space> *" == showType<TypeBinding<OuterSpace*>::value_type>() );
cout << showType<TypeBinding<OuterSpace*>::pointer>() <<endl; CHECK ("Outer<Space>*&" == showType<TypeBinding<OuterSpace*>::reference>() );
CHECK ("Outer<Space>* *" == showType<TypeBinding<OuterSpace*>::pointer>() );
cout << showType<TypeBinding<const OuterSpace*>::value_type>() <<endl; CHECK ("const Outer<Space> *" == showType<TypeBinding<const OuterSpace*>::value_type>() );
cout << showType<TypeBinding<const OuterSpace*>::reference>() <<endl; CHECK ("Outer<Space> const*&" == showType<TypeBinding<const OuterSpace*>::reference>() );
cout << showType<TypeBinding<const OuterSpace*>::pointer>() <<endl; CHECK ("Outer<Space> const* *" == showType<TypeBinding<const OuterSpace*>::pointer>() );
cout << showType<TypeBinding<const OuterSpace * const>::value_type>() <<endl; CHECK ("const Outer<Space> * const" == showType<TypeBinding<const OuterSpace * const>::value_type>() );
cout << showType<TypeBinding<const OuterSpace * const>::reference>() <<endl; CHECK ("Outer<Space> const* const&" == showType<TypeBinding<const OuterSpace * const>::reference>() );
cout << showType<TypeBinding<const OuterSpace * const>::pointer>() <<endl; CHECK ("Outer<Space> * const *" == showType<TypeBinding<const OuterSpace * const>::pointer>() );
cout << showType<TypeBinding<OuterSpace * const>::value_type>() <<endl; CHECK ("Outer<Space> * const" == showType<TypeBinding<OuterSpace * const>::value_type>() );
cout << showType<TypeBinding<OuterSpace * const>::reference>() <<endl; CHECK ("Outer<Space>* const&" == showType<TypeBinding<OuterSpace * const>::reference>() );
cout << showType<TypeBinding<OuterSpace * const>::pointer>() <<endl; CHECK ("Outer<Space> * const *" == showType<TypeBinding<OuterSpace * const>::pointer>() );
cout << showType<TypeBinding<Join>::value_type>() <<endl; // similar for a type without nested type bindings: references are levelled
cout << showType<TypeBinding<Join>::reference>() <<endl; CHECK ("unsigned long" == showType<TypeBinding<Join>::value_type>() );
cout << showType<TypeBinding<Join>::pointer>() <<endl; CHECK ("unsigned long&" == showType<TypeBinding<Join>::reference>() );
CHECK ("unsigned long *" == showType<TypeBinding<Join>::pointer>() );
cout << showType<TypeBinding<Join&>::value_type>() <<endl; CHECK ("unsigned long" == showType<TypeBinding<Join&>::value_type>() );
cout << showType<TypeBinding<Join&>::reference>() <<endl; CHECK ("unsigned long&" == showType<TypeBinding<Join&>::reference>() );
cout << showType<TypeBinding<Join&>::pointer>() <<endl; CHECK ("unsigned long *" == showType<TypeBinding<Join&>::pointer>() );
cout << showType<TypeBinding<Join&&>::value_type>() <<endl; CHECK ("unsigned long" == showType<TypeBinding<Join&&>::value_type>() );
cout << showType<TypeBinding<Join&&>::reference>() <<endl; CHECK ("unsigned long&" == showType<TypeBinding<Join&&>::reference>() );
cout << showType<TypeBinding<Join&&>::pointer>() <<endl; CHECK ("unsigned long *" == showType<TypeBinding<Join&&>::pointer>() );
cout << showType<TypeBinding<Join const&>::value_type>() <<endl; CHECK ("unsigned long" == showType<TypeBinding<Join const&>::value_type>() );
cout << showType<TypeBinding<Join const&>::reference>() <<endl; CHECK ("unsigned long const&" == showType<TypeBinding<Join const&>::reference>() );
cout << showType<TypeBinding<Join const&>::pointer>() <<endl; CHECK ("const unsigned long *" == showType<TypeBinding<Join const&>::pointer>() );
cout << showType<TypeBinding<Join *>::value_type>() <<endl; //... but pointer types are not treated special in any way
cout << showType<TypeBinding<Join *>::reference>() <<endl; CHECK ("unsigned long *" == showType<TypeBinding<Join *>::value_type>() );
cout << showType<TypeBinding<Join *>::pointer>() <<endl; CHECK ("unsigned long*&" == showType<TypeBinding<Join *>::reference>() );
CHECK ("unsigned long* *" == showType<TypeBinding<Join *>::pointer>() );
cout << showType<TypeBinding<const Join *>::value_type>() <<endl; CHECK ("const unsigned long *" == showType<TypeBinding<const Join *>::value_type>() );
cout << showType<TypeBinding<const Join *>::reference>() <<endl; CHECK ("unsigned long const*&" == showType<TypeBinding<const Join *>::reference>() );
cout << showType<TypeBinding<const Join *>::pointer>() <<endl; CHECK ("unsigned long const* *" == showType<TypeBinding<const Join *>::pointer>() );
cout << showType<TypeBinding<const Join * const>::value_type>() <<endl; CHECK ("const unsigned long * const" == showType<TypeBinding<const Join * const>::value_type>() );
cout << showType<TypeBinding<const Join * const>::reference>() <<endl; CHECK ("unsigned long const* const&" == showType<TypeBinding<const Join * const>::reference>() );
cout << showType<TypeBinding<const Join * const>::pointer>() <<endl; CHECK ("unsigned long * const *" == showType<TypeBinding<const Join * const>::pointer>() );
cout << showType<TypeBinding<Join * const>::value_type>() <<endl; CHECK ("unsigned long * const" == showType<TypeBinding<Join * const>::value_type>() );
cout << showType<TypeBinding<Join * const>::reference>() <<endl; CHECK ("unsigned long* const&" == showType<TypeBinding<Join * const>::reference>() );
cout << showType<TypeBinding<Join * const>::pointer>() <<endl; CHECK ("unsigned long * const *" == showType<TypeBinding<Join * const>::pointer>() );
cout << showType<int>() <<endl;
cout << showType<int&>() <<endl;
cout << showType<int&&>() <<endl;
cout << showType<int const&>() <<endl;
cout << showType<int const&&>() <<endl;
cout << showType<int *>() <<endl;
cout << showType<int const *>() <<endl;
cout << showType<int const * const>() <<endl;
cout << showType<int const * &>() <<endl;
cout << showType<int const * const&>() <<endl;
} }
}; };