| Home | Libraries | People | FAQ | More

## Concepts in Depth

### Defining Custom Concepts

(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.

### Overloading

(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.

### Concept Maps

(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
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.

### Associated Types

(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