// Boost.TypeErasure library
//
// Copyright 2012 Steven Watanabe
//
// Distributed under the Boost Software License Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// $Id$

#include <boost/type_erasure/any.hpp>
#include <boost/type_erasure/any_cast.hpp>
#include <boost/type_erasure/builtin.hpp>
#include <boost/type_erasure/operators.hpp>
#include <boost/type_erasure/deduced.hpp>
#include <boost/type_erasure/same_type.hpp>
#include <boost/pointee.hpp>
#include <boost/mpl/vector.hpp>
#include <iostream>

namespace mpl = boost::mpl;
using namespace boost::type_erasure;

//[associated1
/*`
    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;
};

//]

void associated2() {
    //[associated2
    /*`
        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
    //]
}

void associated3() {
    //[associated3
    /*`
        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`.
    */
    //]
}

void associated4() {
    //[associated4
    /*`
        __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
    //]
}

//[associated
//` (For the source of the examples in this section see
//` [@boost:/libs/type_erasure/example/associated.cpp associated.cpp])
//` [associated1]
//` [associated2]
//` [associated3]
//` [associated4]
//]
