Search code examples
c++metaprogrammingboost-mpl

Combination of types using boost::mpl


I have a list of types, from which I want to construct the list of all combinations with two elements. For example:

namespace mpl = boost::mpl;
typedef mpl::vector<int, long> typelist;
// mpl magic...
// the wanted list is equivalent to:
typedef mpl::vector<pair<int, int>, pair<int, long>,
                    pair<long, int>, pair<long, long> > combinations;

Here, pair<T1,T2> could be std::pair<T1,T2>, or mpl::vector<T1,T2>. How to do this? I would also be interested in removing the duplicates when we consider that pair<T1, T2> == pair<T2, T1>.
Thanks.


Solution

  • The list of combinations of a single type int with the list of types mpl::vector<int, long> can be computed by invoking mpl::fold:

    typedef fold<
        mpl::vector<int, long>, vector<>, 
        push_back<mpl::_1, std::pair<int, mpl::_2> > 
    >::type list_of_pairs;
    

    Now, if we wrap that into a separate meta-function and invoke it for all types of the initial typelist we get:

    typedef mpl::vector<int, long> typelist;
    
    template <typename T, typename Result>
    struct list_of_pairs
      : mpl::fold<typelist, Result, 
            mpl::push_back<mpl::_1, std::pair<T, mpl::_2> > > 
    {};
    
    typedef mpl::fold<
        typelist, mpl::vector<>, mpl::lambda<list_of_pairs<mpl::_2, mpl::_1> >
    >::type result_type;
    
    BOOST_MPL_ASSERT(
        mpl::equal<result_type, 
            mpl::vector4<
                std::pair<int, int>, std::pair<int,long>,
                std::pair<long,int>, std::pair<long,long> 
            > >::value);
    

    EDIT: answering second question:

    Making the result containing only unique elements (in the sense you mentioned) is a bit more involved. First you need to define a meta function comparing two elements and returning mpl::true_/mpl::false_:

    template <typename P1, typename P2>
    struct pairs_are_equal
      : mpl::or_<
            mpl::and_<
                is_same<typename P1::first_type, typename P2::first_type>,
                is_same<typename P1::second_type, typename P2::second_type> >,
            mpl::and_<
                is_same<typename P1::first_type, typename P2::second_type>, 
                is_same<typename P1::second_type, typename P2::first_type> > >
    {};
    

    Then we need to define a meta-function which tries to find a given element in a given list:

    template <typename List, typename T>
    struct list_doesnt_have_element
      : is_same<
            typename mpl::find_if<List, pairs_are_equal<mpl::_1, T> >::type, 
            typename mpl::end<List>::type>
    {};
    

    Now, this can be utilized to build a new list, making sure no duplicates are inserted:

    typedef mpl::fold<
        result_type, mpl::vector<>,
        mpl::if_<
            mpl::lambda<list_doesnt_have_element<mpl::_1, mpl::_2> >, 
            mpl::push_back<mpl::_1, mpl::_2>, mpl::_1>
    
    >::type unique_result_type;
    

    All this is from the top of my head, so it may need some tweaking here or there. But the idea should be correct.


    EDIT: minor corrections as outlined by @rafak