Creating all template permutations with MPL
Asked Answered
L

2

9

I have the following templated class structure

struct TraitA{};
struct TraitB{};

template<typename trait>
struct FunctionalityA{};

template<typename trait>
struct FunctionalityB{};

template<typename Func>
struct FuncUserA{};

template<typename Func>
struct FuncUserB{};

template<typename fuser>
struct Host{};

The Host class can now how have the following types.

typedef Host<FuncUserA<FunctionalityA<TraitA> > > Host1_t;
typedef Host<FuncUserA<FunctionalityA<TraitB> > > Host2_t;
typedef Host<FuncUserA<FunctionalityB<TraitA> > > Host3_t;
typedef Host<FuncUserA<FunctionalityB<TraitB> > > Host4_t;
typedef Host<FuncUserB<FunctionalityA<TraitA> > > Host5_t;
typedef Host<FuncUserB<FunctionalityA<TraitB> > > Host6_t;
typedef Host<FuncUserB<FunctionalityB<TraitA> > > Host7_t;
typedef Host<FuncUserB<FunctionalityB<TraitB> > > Host8_t;

Is there any way to create a type list with boost::mpl? At the moment I don't even have an Idea where to start. My Goal would be to have a function like this:

template<class T>
T* getHost()
{
  typedef boost::mpl::find<HostVector, T>::type MplIter;
  return new MplIter;
}

Is this possible with boost::mpl?

Limitative answered 6/5, 2011 at 8:47 Comment(3)
what do you want to do with T w/r to Host ?Halonna
It is just an example. I have a big switch statement that creates the correct Host. I would like to call getHost<5>() and get the type Host5_t. I just want to get rid of the typedefs for all the hosts. Becaues I have far more than in the example and I am afraid at the end of the project there will be even more.Limitative
I think I am missing something here as numerous users have corrected me after I fixed the title to say cartesian product instead of permutation. I am happy to risk embarrassment for getting an aha experience. Why everyone insists this is about permutation?Steed
H
5

Ok here is some implementation. It is rather ad-hoc, one could obviously abstract it to take a sequence of sequence of lambda function, but I preferred keeping this one straight. Comments and tests are inside:

#include <iostream>

////////////////////////////////////////////////////////////////////////////////
// Base types to permute around - added soem display for tests purpose
////////////////////////////////////////////////////////////////////////////////
struct TraitA { TraitA() { std::cout << "TA"; } };
struct TraitB { TraitB() { std::cout << "TB"; } };

template<typename Trait>
struct FunctionalityA
{
  FunctionalityA() { std::cout << "FunctionalityA<"; Trait(); std::cout << ">";}
};

template<typename Trait>
struct FunctionalityB
{
  FunctionalityB() { std::cout << "FunctionalityB<"; Trait(); std::cout << ">";}
};

template<typename Func>
struct FuncUserA
{
  FuncUserA() { std::cout << "FuncUserA<"; Func(); std::cout << ">";}
};

template<typename Func>
struct FuncUserB
{
  FuncUserB() { std::cout << "FuncUserB<"; Func(); std::cout << ">";}
};

template<typename Fuser> struct Host
{
  Host() { std::cout << "Host<"; Fuser(); std::cout << ">\n";}
};

////////////////////////////////////////////////////////////////////////////////
// Step 1 : Make static list of potential options
//
// These lists has to be updated as new Trait, FuncUser and Functionality are
// made.
////////////////////////////////////////////////////////////////////////////////
#include <boost/mpl/vector.hpp>
#include <boost/mpl/placeholders.hpp>

typedef boost::mpl::vector< TraitA, TraitB >              traits_list;
typedef boost::mpl::vector< FunctionalityA<boost::mpl::_>
                          , FunctionalityB<boost::mpl::_>
                          >                                functionalities_list;

typedef boost::mpl::vector< FuncUserA<boost::mpl::_>
                          , FuncUserB<boost::mpl::_>
                          >                                fusers_list;

////////////////////////////////////////////////////////////////////////////////
// Step 1 : Build the types
//
// We want every combination of Trait and Functionality. This is basically a
// cartesian product of traits_list and functionalities_list which is done
// usign nested fold
////////////////////////////////////////////////////////////////////////////////
#include <boost/mpl/fold.hpp>
#include <boost/mpl/copy.hpp>
#include <boost/mpl/push_back.hpp>
#include <boost/mpl/back_inserter.hpp>

template<typename Fusers, typename Functionalities, typename Traits>
struct build_combo
{
  //////////////////////////////////////////////////////////////////////////////
  // Inner fold loop iterating over the traits
  //////////////////////////////////////////////////////////////////////////////
  template<typename FuserFunc>
  struct traits_loop
  {
    template<typename T>
    struct fuse : boost::mpl::apply<FuserFunc,T> {};

    typedef typename
    boost::mpl::fold< Traits
                    , boost::mpl::vector<>
                    , boost::mpl::push_back < boost::mpl::_1
                                            , fuse<boost::mpl::_2>
                                            >
                    >::type type;
  };

  //////////////////////////////////////////////////////////////////////////////
  // Inner fold loop iterating over the functionnality/traits
  //////////////////////////////////////////////////////////////////////////////
  template<typename Fuser>
  struct func_traits_loop
  {
    template<typename T>
    struct fuse : boost::mpl::apply<Fuser,T> {};

    typedef typename
    boost::mpl::fold< Functionalities
                    , boost::mpl::vector<>
                    , boost::mpl::copy< traits_loop< fuse<boost::mpl::_2> >
                                      , boost::mpl::back_inserter<boost::mpl::_1>
                                      >
                    >::type type;
  };

  //////////////////////////////////////////////////////////////////////////////
  // fold loop iterating over the Fuser x {Functionality,Traits}
  // For each Fuser, copy its vector of applications to the others
  //////////////////////////////////////////////////////////////////////////////
  typedef typename
  boost::mpl::fold< Fusers
                  , boost::mpl::vector<>
                  , boost::mpl::copy< func_traits_loop<boost::mpl::_2>
                                    , boost::mpl::back_inserter<boost::mpl::_1>
                                    >
                  >::type type;
};

////////////////////////////////////////////////////////////////////////////////
// Now the get_host meta-function
////////////////////////////////////////////////////////////////////////////////
#include <boost/mpl/at.hpp>

template<int N>
struct get_host
{
  typedef build_combo < fusers_list
                      , functionalities_list
                      , traits_list
                      >::type                     types;
  typedef typename boost::mpl::at_c<types,N>::type hosted;
  typedef Host<hosted> type;
};

////////////////////////////////////////////////////////////////////////////////
// Some tests
////////////////////////////////////////////////////////////////////////////////

int main()
{
  get_host<1>::type x1;
  get_host<2>::type x2;
  get_host<3>::type x3;
  get_host<4>::type x4;
  get_host<5>::type x5;
  get_host<6>::type x6;
  get_host<7>::type x7;
}

The expected output should be :

Host<FuncUserA<FunctionalityA<TB>>>
Host<FuncUserA<FunctionalityB<TA>>>
Host<FuncUserA<FunctionalityB<TB>>>
Host<FuncUserB<FunctionalityA<TA>>>
Host<FuncUserB<FunctionalityA<TB>>>
Host<FuncUserB<FunctionalityB<TA>>>
Host<FuncUserB<FunctionalityB<TB>>>
Halonna answered 7/5, 2011 at 16:3 Comment(3)
Unbelievable. This is exactly what I want. I guess I need to work on my template skills.Limitative
I have one last question left. What exactly does the boost::mpl::apply<FuserFunc,T> do? I tried to use some other apply function but I cannot figure out how mpl::apply works.Limitative
apply takes a metafunction and a type and call the metafunction over T. It's basically a generic way to apply metafunctions over a set of templates arguments. If we made a runtime function analogy is like doing call( some_fn, arg0, arg1, arg2 )Halonna
S
3

Introduction

My answer tries to be the prefered one for C++11 users.

Joel Falcou's answer is great for the older standard but C++11's parameter packs often make boost type sequences obsolete. Furthermore I think piggy backing templates is better in this case than using boost::lambda. Actually my solution uses no includes at all except the Cartesian product template which I will take from here as it is not in the standard library.

Using the currently newest features (as of C++11) allows coding a solution that:

  1. Scales better.
  2. Is shorter
  3. Is more readable because the solution separates the concerns.

The different templates explained:

expand_pack executes its parameters. This allowes to repeat runtime code using ellipsis. Example: expand_pack(new T{}...). Anyone knows the name of this idiom?

wrap_template_as_type piggy backs a template so it can be used where a type is expected. Maybe this idiom is called template rebinding or late template binding. I do not know so I posted this question here. Examples: wrap_template_as_type<map> and the opposite wrapper::unwrapp<int, string>

type_list a tuple without data, bells and wissels.

template_list a template that takes a list of templates and returns a type_list with the original templates piggy backed in a wrapper.

make_host_type converts A, B, C, D into A<B<C<D>>>

all_hosts gets a tuple of Host types and news one Host for each element in the input tuple.

Full Example

Note that #include http://... must be replaced with the linked content

Note that MSVC does not understand PRETTY_FUNCTION but FUNCDNAME I think

#include <iostream>
#include <typeinfo>
#include https://mcmap.net/q/536756/-how-to-create-the-cartesian-product-of-a-type-list
template<typename... Ts> void expand_pack (Ts... t) {}

template <template<typename...> class T>
struct wrapp_template_as_type {
    template <typename... Us> using unwrapp =  T <Us...>;
};

template<typename... T> struct type_list {};

template <template<typename...> class... Ts>
struct template_list {
    using type = type_list< wrapp_template_as_type<Ts>... >;
};

struct TraitA{}; struct TraitB{}; struct TraitC{}; struct TraitD{}; struct TraitE{}; struct TraitF{};
template<typename Trait>    struct  WhateverA{};
template<typename Trait>    struct  WhateverB{};
template<typename Whatever> struct  FunctionalityA{};
template<typename Whatever> struct  FunctionalityB{};
template<typename Whatever> struct  FunctionalityC{};
template<typename Whatever> struct  FunctionalityD{};
template<typename Func>     struct  FuncUserA{};
template<typename Func>     struct  FuncUserB{};
template<typename Func>     struct  FuncUserC{};
template<typename FuncUser> struct  Host { Host() {std::cout << __PRETTY_FUNCTION__ << std::endl;}};

template<typename T> struct make_host_type;

template<template<typename...> class List, typename T, typename... Ts>
struct make_host_type < List<T, Ts...> > {
    using type = typename T::template unwrapp < typename make_host_type< List<Ts...> >::type >;
};

template<template<typename...> class List, typename T>
struct make_host_type < List<T> > {
    using type = T;
};

template <typename T> struct all_hosts;

template <template<typename...> class Hosts, typename... Ts>
struct all_hosts <Hosts<Ts...> > {
    static void create () {
        expand_pack (new typename make_host_type<Ts>::type{}...);
    }
};

int main () {
    using a =          type_list     < TraitA, TraitB, TraitC, TraitD, TraitE, TraitF >;
    using b = typename template_list < WhateverA, WhateverB>::type;
    using c = typename template_list < FunctionalityA, FunctionalityB, FunctionalityC, FunctionalityD >::type;
    using d = typename template_list < FuncUserA, FuncUserB, FuncUserC >::type;
    using e = typename template_list < Host >::type;
    using p = typename product<type_list, e, d, c, b, a>::type; // create a type_list of all possible Host types.
    all_hosts<p>::create(); // calls constructor for each Host type
}

Output is: (If you use MSVC replace PRETTY_FUNCTION with something else)

Host<FuncUser>::Host() [with FuncUser = FuncUserC<FunctionalityD<WhateverB<TraitF> > >]
Host<FuncUser>::Host() [with FuncUser = FuncUserB<FunctionalityD<WhateverB<TraitF> > >]
Host<FuncUser>::Host() [with FuncUser = FuncUserA<FunctionalityD<WhateverB<TraitF> > >]
Host<FuncUser>::Host() [with FuncUser = FuncUserC<FunctionalityC<WhateverB<TraitF> > >]
Host<FuncUser>::Host() [with FuncUser = FuncUserB<FunctionalityC<WhateverB<TraitF> > >]
Host<FuncUser>::Host() [with FuncUser = FuncUserA<FunctionalityC<WhateverB<TraitF> > >]
Host<FuncUser>::Host() [with FuncUser = FuncUserC<FunctionalityB<WhateverB<TraitF> > >]
Host<FuncUser>::Host() [with FuncUser = FuncUserB<FunctionalityB<WhateverB<TraitF> > >]
...
Host<FuncUser>::Host() [with FuncUser = FuncUserA<FunctionalityA<WhateverA<TraitA> > >]
Steed answered 12/11, 2013 at 6:56 Comment(4)
The proposal link is brokenEspino
@alfC, no it's not broken but it has been marked as off topic by our moderators. The link still works for me. Yes, they are very strict 8-)Steed
@PatrickFromberg If possible, I would like to know what happened to the proposal for the Cartesian product. Has the code been integrated into one of the existing libraries?Tirewoman
@Tirewoman I had no responses on this and I must admit that it is not really something you can make very much use of. It's just an interesting exercise that I did with Boost and now got much easier with the introduction of variadic template parameters.Steed

© 2022 - 2024 — McMap. All rights reserved.