|
Boost Users : |
From: Tobias Schwinger (tschwinger_at_[hidden])
Date: 2008-01-03 20:12:32
dariomt_at_[hidden] 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:
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;
}
Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net