Boost logo

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:

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


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