
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; }