|  | Home | Libraries | People | FAQ | More | 
(For the source of the examples in this section see custom.cpp)
        Earlier, we used BOOST_TYPE_ERASURE_MEMBER
        to define a concept for containers that support push_back.
        Sometimes this interface isn't flexible enough, however. The library also
        provides a lower level interface that gives full control of the behavior.
        Let's take a look at what we would need in order to define has_push_back.
        First, we need to define the has_push_back
        template itself. We'll give it two template parameters, one for the container
        and one for the element type. This template must have a static member function
        called apply which is used to execute the operation.
      
template<class C, class T> struct has_push_back { static void apply(C& cont, const T& arg) { cont.push_back(arg); } };
        Now, we can use this in an any
        using call to
        dispatch the operation.
      
std::vector<int> vec; any<has_push_back<_self, int>, _self&> c(vec); int i = 10; call(has_push_back<_self, int>(), c, i); // vec is [10].
        Our second task is to customize any
        so that we can call c.push_back(10). We do
        this by specializing concept_interface.
        The first argument is has_push_back,
        since we want to inject a member into every any
        that uses the has_push_back
        concept. The second argument, Base,
        is used by the library to chain multiple uses of concept_interface
        together. We have to inherit from it publicly. Base
        is also used to get access to the full any
        type. The third argument is the placeholder that represents this any. If
        someone used push_back<_c, _b>, we only want to insert a push_back member in the container, not
        the value type. Thus, the third argument is the container placeholder.
      
        When we define push_back
        the argument type uses the metafunction as_param.
        This is just to handle the case where T
        is a placeholder. If T is
        not a placeholder, then the metafunction just returns its argument, const T&, unchanged.
      
namespace boost { namespace type_erasure { template<class C, class T, class Base> struct concept_interface<has_push_back<C, T>, Base, C> : Base { void push_back(typename as_param<Base, const T&>::type arg) { call(has_push_back<C, T>(), *this, arg); } }; } }
Our example now becomes
std::vector<int> vec; any<has_push_back<_self, int>, _self&> c(vec); c.push_back(10);
which is what we want.
(For the source of the examples in this section see overload.cpp)
        concept_interface
        allows us to inject arbitrary declarations into an any.
        This is very flexible, but there are some pitfalls to watch out for. Sometimes
        we want to use the same concept several times with different parameters.
        Specializing concept_interface
        in a way that handles overloads correctly is a bit tricky. Given a concept
        foo, we'd like the following to work:
      
any< mpl::vector< foo<_self, int>, foo<_self, double>, copy_constructible<> > > x = ...; x.foo(1); // calls foo(int) x.foo(1.0); // calls foo(double)
        Because concept_interface
        creates a linear inheritance chain, without some extra work, one overload
        of foo will hide the other.
      
Here are the techniques that I found work reliably.
For member functions I couldn't find a way to avoid using two specializations.
template<class T, class U> struct foo { static void apply(T& t, const U& u) { t.foo(u); } }; namespace boost { namespace type_erasure { template<class T, class U, class Base, class Enable> struct concept_interface< ::foo<T, U>, Base, T, Enable> : Base { typedef void _fun_defined; void foo(typename as_param<Base, const U&>::type arg) { call(::foo<T, U>(), *this, arg); } }; template<class T, class U, class Base> struct concept_interface< ::foo<T, U>, Base, T, typename Base::_fun_defined> : Base { using Base::foo; void foo(typename as_param<Base, const U&>::type arg) { call(::foo<T, U>(), *this, arg); } }; } }
        This uses SFINAE to detect whether a using declaration is needed. Note that
        the fourth argument of concept_interface
        is a dummy parameter which is always void and is intended to be used for
        SFINAE. Another solution to the problem that I've used in the past is to
        inject a dummy declaration of fun
        and always put in a using declaration. This is an inferior solution for several
        reasons. It requires an extra interface to add the dummy overload. It also
        means that fun is always
        overloaded, even if the user only asked for one overload. This makes it harder
        to take the address of fun.
      
        Note that while using SFINAE requires some code to be duplicated, the amount
        of code that has to be duplicated is relatively small, since the implementation
        of concept_interface
        is usually a one liner. It's a bit annoying, but I believe it's an acceptable
        cost in lieu of a better solution.
      
For free functions you can use inline friends.
template<class T, class U> struct bar_concept { static void apply(T& t, const U& u) { bar(t, u); } }; namespace boost { namespace type_erasure { template<class T, class U, class Base> struct concept_interface< ::bar_concept<T, U>, Base, T> : Base { friend void bar(typename derived<Base>::type& t, typename as_param<Base, const U&>::type u) { call(::bar_concept<T, U>(), t, u); } }; template<class T, class U, class Base> struct concept_interface< ::bar_concept<T, U>, Base, U, typename boost::disable_if<is_placeholder<T> >::type> : Base { using Base::bar; friend void bar(T& t, const typename derived<Base>::type& u) { call(::bar_concept<T, U>(), t, u); } }; } }
        Basically we have to specialize concept_interface
        once for each argument to make sure that an overload is injected into the
        first argument that's a placeholder. As you might have noticed, the argument
        types are a bit tricky. In the first specialization, the first argument uses
        derived instead
        of as_param. The
        reason for this is that if we used as_param,
        then we could end up violating the one definition rule by defining the same
        function twice. Similarly, we use SFINAE in the second specialization to
        make sure that bar is only defined once when both arguments are placeholders.
        It's possible to merge the two specializations with a bit of metaprogramming,
        but unless you have a lot of arguments, it's probably not worth while.
      
(For the source of the examples in this section see concept_map.cpp)
        Sometimes it is useful to non-intrusively adapt a type to model a concept.
        For example, suppose that we want to make std::type_info
        model less_than_comparable.
        To do this, we simply specialize the concept definition.
      
namespace boost { namespace type_erasure { template<> struct less_than_comparable<std::type_info> { static bool apply(const std::type_info& lhs, const std::type_info& rhs) { return lhs.before(rhs) != 0; } }; } }
| ![[Note]](../../../doc/src/images/note.png) | Note | 
|---|---|
| Most, but not all of the builtin concepts can be specialized. Constructors, destructors, and RTTI need special treatment from the library and cannot be specialized. Only primitive concepts can be specialized, so the iterator concepts are also out. | 
(For the source of the examples in this section see associated.cpp)
        Associated types such as typename
        T::value_type or typename
        std::iterator_traits<T>::reference are quite common in template
        programming. Boost.TypeErasure handles them using the deduced
        template. deduced
        is just like an ordinary placeholder,
        except that the type that it binds to is determined by calling a metafunction
        and does not need to be specified explicitly.
      
        For example, we can define a concept for holding an iterator, raw pointer,
        or smart pointer as follows. First, we define a metafunction called pointee defining the associated type.
      
template<class T> struct pointee { typedef typename mpl::eval_if<is_placeholder<T>, mpl::identity<void>, boost::pointee<T> >::type type; };
        Note that we can't just use boost::pointee,
        because this metafunction needs to be safe to instantiate with placeholders.
        It doesn't matter what it returns as long as it doesn't give an error. (The
        library never tries to instantiate it with a placeholder, but argument dependent
        lookup can cause spurious instantiations.)
      
template<class T = _self> struct pointer : mpl::vector< copy_constructible<T>, dereferenceable<deduced<pointee<T> >&, T> > { // provide a typedef for convenience typedef deduced<pointee<T> > element_type; };
        Now the Concept of x uses
        two placeholders, _self and
        pointer<>::element_type. When we construct x, with an int*, pointer<>::element_type
        is deduced as pointee<int*>::type which is int.
        Thus, dereferencing x returns
        an any that contains
        an int.
      
int i = 10; any< mpl::vector< pointer<>, typeid_<pointer<>::element_type> > > x(&i); int j = any_cast<int>(*x); // j == i
        Sometimes we want to require that the associated type be a specific type.
        This can be solved using the same_type
        concept. Here we create an any that can hold any pointer whose element type
        is int.
      
int i = 10; any< mpl::vector< pointer<>, same_type<pointer<>::element_type, int> > > x(&i); std::cout << *x << std::endl; // prints 10
        Using same_type
        like this effectively causes the library to replace all uses of pointer<>::element_type with int
        and validate that it is always bound to int.
        Thus, dereferencing x now
        returns an int.
      
        same_type can
        also be used for two placeholders. This allows us to use a simple name instead
        of writing out an associated type over and over.
      
int i = 10; any< mpl::vector< pointer<>, same_type<pointer<>::element_type, _a>, typeid_<_a>, copy_constructible<_a>, addable<_a>, ostreamable<std::ostream, _a> > > x(&i); std::cout << (*x + *x) << std::endl; // prints 20