[Boost.MPL and Boost.Fusion] Combinatorial game with typelists

Hello boost users, I need some advice in using MPL and fusion. Lets take the following case for a typical Factory pattern (full compilable source code is attached): class Base {...}; class Factory {...}; template <typename T> struct Helper {...}; // constructor registers creation function for template parameter Now we have a template defining a family of derived classes: template <typename U, typename V, typename W> class DerivedUVW : public Base {...}; And a helper registration macro: #define HELPER_DERIVED_UVW(U, V, W) \ Helper<DerivedUVW<U, V, W> > BOOST_PP_CAT(h, __LINE__) (#U #V #W); And all possible U V and W classes in the combination game: struct A1 {}; struct A2 {}; // for U struct B1 {}; struct B2 {}; // for V struct C1 {}; struct C2 {}; // for W Now with manual registration its just a matter of patience: // Manual registration: // must type all possible combinations -> ugly HELPER_DERIVED_UVW(A1, B1, C1); HELPER_DERIVED_UVW(A1, B1, C2); // etc... To create the desired object just use Factory::instance().create("A1B1C1")->doInterestingStuff() And now the six million dollar question: Is there a way in which MPL and fusion can automatically do the registration? Here registration means the instantiation of template Helper with all possible combinations of DerivedUVW with elements from lists like: typedef boost::mpl::vector<A1,A2> Ulist; typedef boost::mpl::vector<B1,B2> Vlist; typedef boost::mpl::vector<C1,C2> Wlist; and registered with an appropiate name like "A1B1C1" Of course the objective is to easily add A3, A4, B3, C4, etc... to allow more combinations, without having to manually update all the registration code, ideally just the typelists. And, let's not forget variants DerivedUV or DerivedUVWZ with a different number of template parameters. I'm trying to think how I would do combinations for values in runtime and translate it to the MPL domain. Still no success. Any help, suggestions or comments will be appreciated. PS: the context of this problem is being able to expose a heavily templatized library to a user selecting the combination of template classes at runtime with a text string. I'm sure there's a suitable solution to this problem somewhere out there, or at least some hints to build my own solution...

I personally solved this with BOOST.Preprocessor to generate the many combinations, but I think you could make an MPL/Fusion solution. Chris On Jan 3, 2008 1:03 PM, <dariomt@gmail.com> wrote:
Hello boost users,
I need some advice in using MPL and fusion.
Lets take the following case for a typical Factory pattern (full compilable source code is attached):
class Base {...}; class Factory {...}; template <typename T> struct Helper {...}; // constructor registers creation function for template parameter
Now we have a template defining a family of derived classes: template <typename U, typename V, typename W> class DerivedUVW : public Base {...};
And a helper registration macro: #define HELPER_DERIVED_UVW(U, V, W) \ Helper<DerivedUVW<U, V, W> > BOOST_PP_CAT(h, __LINE__) (#U #V #W);
And all possible U V and W classes in the combination game: struct A1 {}; struct A2 {}; // for U struct B1 {}; struct B2 {}; // for V struct C1 {}; struct C2 {}; // for W
Now with manual registration its just a matter of patience: // Manual registration: // must type all possible combinations -> ugly HELPER_DERIVED_UVW(A1, B1, C1); HELPER_DERIVED_UVW(A1, B1, C2); // etc...
To create the desired object just use Factory::instance().create("A1B1C1")->doInterestingStuff()
And now the six million dollar question: Is there a way in which MPL and fusion can automatically do the registration?
Here registration means the instantiation of template Helper with all possible combinations of DerivedUVW with elements from lists like: typedef boost::mpl::vector<A1,A2> Ulist; typedef boost::mpl::vector<B1,B2> Vlist; typedef boost::mpl::vector<C1,C2> Wlist; and registered with an appropiate name like "A1B1C1"
Of course the objective is to easily add A3, A4, B3, C4, etc... to allow more combinations, without having to manually update all the registration code, ideally just the typelists. And, let's not forget variants DerivedUV or DerivedUVWZ with a different number of template parameters.
I'm trying to think how I would do combinations for values in runtime and translate it to the MPL domain. Still no success.
Any help, suggestions or comments will be appreciated.
PS: the context of this problem is being able to expose a heavily templatized library to a user selecting the combination of template classes at runtime with a text string. I'm sure there's a suitable solution to this problem somewhere out there, or at least some hints to build my own solution...
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users

dariomt@gmail.com wrote:
class Base {...}; class Factory {...}; template <typename T> struct Helper {...}; // constructor registers creation function for template parameter
Now we have a template defining a family of derived classes: template <typename U, typename V, typename W> class DerivedUVW : public Base {...};
...
And all possible U V and W classes in the combination game: struct A1 {}; struct A2 {}; // for U struct B1 {}; struct B2 {}; // for V struct C1 {}; struct C2 {}; // for W
...
And now the six million dollar question: Is there a way in which MPL and fusion can automatically do the registration?
I think so (see attachment)! Here's an MPL-based solution. It does not require any preprocessor code and compiles with GCC4. Just for the fun of it, I reduced the Factory code to a one-liner using the recently reviewed factory functional, which can be downloaded here: http://tinyurl.com/35vlvb Regards, Tobias #include <iostream> #include <sstream> #include <string> #include <memory> #include <map> #include <boost/function.hpp> #include <boost/shared_ptr.hpp> #include <boost/functional/factory.hpp> #include <boost/mpl/bool.hpp> #include <boost/mpl/begin.hpp> #include <boost/mpl/end.hpp> #include <boost/mpl/next.hpp> #include <boost/mpl/deref.hpp> #include <boost/mpl/pair.hpp> #include <boost/mpl/fold.hpp> #include <boost/mpl/vector.hpp> #include <boost/mpl/push_back.hpp> #include <boost/mpl/unpack_args.hpp> #include <boost/mpl/placeholders.hpp> #include <boost/mpl/transform.hpp> #include <boost/mpl/transform_view.hpp> #include <boost/mpl/print.hpp> // Class hierarchy: class base { public: virtual void introduce_yourself() = 0; virtual ~base() { } }; template< class U, class V, class W > class derived : public base { public: static std::string name() { // not using typeid(derived<U,V,W>).name() since ABI-dependent static std::string* result; if (! result) { std::ostringstream s; s << "derived<" << U::name() << "," << V::name() << "," << W::name() << ">"; result = new std::string(s.str()); } return *result; } void introduce_yourself() { std::cout << "Hi I'm " << name() << "." << std::endl; } }; // Arguments for 'derived<U,V,W>' (note I could've used some traits class // for the names): struct A1 { static char const* name() { return "A1"; } }; struct A2 { static char const* name() { return "A2"; } }; struct B1 { static char const* name() { return "B1"; } }; struct B2 { static char const* name() { return "B2"; } }; struct C1 { static char const* name() { return "C1"; } }; struct C2 { static char const* name() { return "C2"; } }; // Factory pattern reduced to its essence, using the recently reviewed // function object: std::map< std::string, boost::function<boost::shared_ptr<base>()> > factories; // Metaprogramming namespace mpl = boost::mpl; using namespace mpl::placeholders; // Let's call an iterator and a range a "cursor" template< class Iter, class From, class To > struct cursor; // We create it from a sequence... template< class Sequence > struct make_cursor { typedef typename mpl::begin<Sequence>::type from; typedef typename mpl::end<Sequence>::type to; typedef cursor<from,from,to> type; }; // ...can dereference it... template< class Cursor > struct deref; template< class Iterator, class From, class To > struct deref< cursor<Iterator,From,To> > : mpl::deref<Iterator> { }; // ...move it forward... template< class Cursor > struct next; template< class Iterator, class From, class To > struct next< cursor<Iterator,From,To> > { typedef cursor< typename mpl::next<Iterator>::type, From, To > type; }; // ...and have it wrap around once it reaces the end. template< class Cursor > struct wrap_around; template< class Iterator, class From, class To > struct wrap_around< cursor<Iterator,From,To> > { typedef cursor<Iterator,From,To> type; typedef mpl::false_ wrapped; }; template< class To, class From > struct wrap_around< cursor<To,From,To> > { typedef cursor<From,From,To> type; typedef mpl::true_ wrapped; }; // State machine intended to be used with mpl::fold that adds moved cursors to // a sequence until one does not hit the end. Remaining cursors are added as-is. struct nested_loops_fold_op { typedef mpl::pair< mpl::true_, mpl::vector<> > initial_state; template< class State, class Cursor > struct apply; template< class Sequence, class Cursor > struct apply< mpl::pair<mpl::true_,Sequence>, Cursor > { typedef typename next<Cursor>::type next_cursor; typedef typename wrap_around<next_cursor>::type new_cursor; typedef typename wrap_around<next_cursor>::wrapped wrapped; typedef typename mpl::push_back<Sequence,new_cursor>::type sequence; typedef mpl::pair< wrapped, sequence > type; }; template< class Sequence, class Cursor > struct apply< mpl::pair<mpl::false_,Sequence>, Cursor > { typedef typename mpl::push_back<Sequence,Cursor>::type sequence; typedef mpl::pair< mpl::false_, sequence > type; }; }; template< class Cursors, class Done > struct register_factories_impl { static inline void call() { // deref cursors and specialize 'derived' template typedef typename mpl::unpack_args< derived<_1,_2,_3> > // typedef typename xyz ::template apply< mpl::transform_view< Cursors, deref<_1> > >::type the_type; // register factory factories[the_type::name()] = boost::factory< std::auto_ptr<the_type> >(); // tail-recurse typedef typename mpl::fold<Cursors, nested_loops_fold_op::initial_state, nested_loops_fold_op>::type next_state; register_factories_impl< typename mpl::second<next_state>::type, typename mpl::first<next_state>::type >::call(); } }; template< class Cursors > struct register_factories_impl< Cursors, mpl::true_ > { static inline void call() { } }; template< class Sequences > inline void register_factories() { typedef typename mpl::transform<Sequences, make_cursor<_1> >::type cursors; typedef typename mpl::fold<cursors, nested_loops_fold_op::initial_state, nested_loops_fold_op>::type next_state; typedef typename mpl::first<next_state>::type done; register_factories_impl<cursors,done>::call(); } int main() { register_factories< mpl::vector< mpl::vector<A1,A2>, mpl::vector<B1,B2>, mpl::vector<C1,C2> > >(); factories["derived<A2,B1,C2>"]()->introduce_yourself(); factories["derived<A2,B2,C1>"]()->introduce_yourself(); // ... return 0; }

Tobias Schwinger wrote:
dariomt@gmail.com wrote:
class Base {...}; class Factory {...}; template <typename T> struct Helper {...}; // constructor registers creation function for template parameter
Now we have a template defining a family of derived classes: template <typename U, typename V, typename W> class DerivedUVW : public Base {...};
...
And all possible U V and W classes in the combination game: struct A1 {}; struct A2 {}; // for U struct B1 {}; struct B2 {}; // for V struct C1 {}; struct C2 {}; // for W
...
And now the six million dollar question: Is there a way in which MPL and fusion can automatically do the registration?
I think so (see attachment)!
Here's an MPL-based solution. It does not require any preprocessor code and compiles with GCC4.
With a bug. It successfully determines whether there is nothing to do - but also does nothing if there's almost nothing to do, that is only one combination. It also won't work if one of the input sequences is empty. The attached patches remove the entry condition. So the precondition is "the sequences yield at least one combination" now. Checking it is left an exercise to the reader :). Regards, Tobias --- combinations.cpp.bak 2008-01-05 10:55:37.000000000 +0100 +++ combinations.cpp 2008-01-05 10:56:04.000000000 +0100 @@ -206,12 +206,7 @@ inline void register_factories() { typedef typename mpl::transform<Sequences, make_cursor<_1> >::type cursors; - - typedef typename mpl::fold<cursors, nested_loops_fold_op::initial_state, - nested_loops_fold_op>::type next_state; - typedef typename mpl::first<next_state>::type done; - - register_factories_impl<cursors,done>::call(); + register_factories_impl<cursors,mpl::false_>::call(); } int main() --- combinations_rt.cpp.bak 2008-01-05 10:55:47.000000000 +0100 +++ combinations_rt.cpp 2008-01-05 10:56:22.000000000 +0100 @@ -63,20 +63,12 @@ cursor_vector cursors; cursors += cursor(l1), cursor(l2), cursor(l3); - // Note: We use the algorithm to determine whether there is something - // to do in the first place (just like the compile time version). - // However, the runtime version works directly on mutable cursors, so - // we copy the sequence to ignore its effect on the cursors. - cursor_vector disposable_copy_of_cursors(cursors); - if (! nested_loops_step(disposable_copy_of_cursors)) + do { - do - { - std::cout << cursors[0].deref() << ',' << cursors[1].deref() << ',' - << cursors[2].deref() << std::endl; + std::cout << cursors[0].deref() << ',' << cursors[1].deref() << ',' + << cursors[2].deref() << std::endl; - } while (! nested_loops_step(cursors)); - } + } while (! nested_loops_step(cursors)); }
participants (3)
-
Chris Weed
-
dariomt@gmail.com
-
Tobias Schwinger