views:

1020

answers:

3

I have used something like the following to compose policies for my application:

The policy classes look like this:

struct Policy {
  static void init();
  static void cleanup();
  //...
};

template <class CarT, class CdrT>
struct Cons {
  static void init() {
    CarT::init();
    CdrT::init();
  }
  static void cleanup() {
    CdrT::cleanup();
    CarT::cleanup();
  }
  //...
};

To compose policies:

typedef Cons<Policy1, Cons<Policy2, Cons<Policy3, Policy4> > > MyPolicy;

To use MyPolicy:

init_with<MyPolicy>(...);
//...
cleanup_with<MyPolicy>(...);

where they'd call:

MyPolicy::init_options(); // calls Policy1 to 4's init in order

and

MyPolicy::cleanup(); // calls Policy1 to 4's cleanup in reverse order

Essentially, Cons constructs a type list here. It's pretty straight forward. However the typedef cons line is kinda ugly. It'll be ideal to have policy combiner that can do this:

typedef CombinePolicy<Policy1, Policy2, Policy3, Policy4> MyPolicy;

Since we can have arbitrary number of policies, the CombinePolicy would need variadic template support in C++0x, which is only available experimentally in cutting edge compilers. However, it seems that boost:mpl library solved/worked around the problem by using a bunch preprocessing tricks. I guess I could use something like:

typedef mpl::list<Policy, Policy2, Policy3, Policy4> Policies;

and then calls:

init_with<Policies>(...);

which would then use:

typedef iter_fold<Policies, begin<Policies>::type,
                  some_magic_lambda_expression>::type MyPolicy;

Obviously, I have a little trouble figuring out *some_magic_lambda_expression* here. I'm sure it's quite trivial for mpl experts here.

Thanks in advance.

+1  A: 

I think your problem is rather runtime invocation than metafunctions, because you want to call the init functions on the actual runtime objects.

You could try the runtime algorithms of mpl, like :

for_each<Policies>(InitPolicy());

with

struct InitPolicy() {
    template<class Policy>
    void operator() (Policy& p) { p.init_options(); }
};
tabdamage
There is a little bug in your example. This could make to work for the example. I can use for_each for each methods. But what I prefer to have one combined policy that can be passed around, ie., I prefer the order be enforced at compile time rather than runtime with for_each.
ididak
As I see it, you can only call metafunctions at compile time, I see no way of calling init_options() or any other plain function at compile time. I understood you want to automagically apply all Policies in the Policy list by calling init_with at runtime, which is what for_each does. please clarify
tabdamage
The goal is to compose a policy class at compile time with the order enforced as in my original example and the actually methods are indeed invoked at runtime like MyCombinedPolicy::init_options() etc.
ididak
To further clarify the point: if I want to have a void cleanup() method for each policy and I want to do the cleanups in the reverse of the order declared in the type list, it's easily done in my original explicit cons scheme but mpl::for_each clearly wouldn't work here.
ididak
+1  A: 

I think you're looking for something like:

typedef 
  iter_fold<
    Policies,
    begin<Policies>::type,
    Cons<_1,_2>
  >::type
  MyType;

You also might want to look into inherit_linearly<> if you buld-in some sort of CRTP to invoke a base's functions hard-wired at compile time.

Dean Michael
That was my original guess, but I don't think this is correct (wouldn't compile etc.) though. Also I don't think inherit_linearly fits the model here. I want to make the composition trivial and declarative. A type sequence would be the easiest.
ididak
+6  A: 

Since no one answered the question satisfactorily, I spent sometime digging into the boost::mpl source. Man, it's not pretty with layers of macros and hundreds of lines of specialization classes. I now have more appreciation for the authors of the boost libraries to make meta programming easier and more portable for us. Hopefully C++0x will make library writers' life easier as well.

Anyway, the solution turns out to be simple and elegant.

First iter_fold is not what I want, as I couldn't figure out how to specify an iterator that can be deferenced to a null type. So I fiddled around with fold and find the following:

typedef fold<Policies, Null, Cons<_1, _2> >::type MyPolicy;

In order for this to work, I need to provide the Null type and a specialization for Cons:

struct Null { };

template<class PolicyT>
struct Cons<Null, PolicyT> {
  static void init() { PolicyT::init(); }
  static void cleanup() { PolicyT::cleanup(); }
};
ididak
elegant, i agree. looks a bit like Alexandrescus type_list. At a certain level, the magic required to make things invisible to users is quite a bother...
tabdamage