views:

152

answers:

2

I've recently tackled the constructor problem, where various mixins classes that decorate each other (and a topmost host class) have different constructor signatures. To maintain a single constructor in the resulting decorated class, and without adding init functions, I've found the following solution. The only restriction it places on a mixin class is that if its constructor takes more than one parameter, they should all be encapsulated in a single tuple. (Compiling this code with g++ requires the -std=c++0x flags)

#include <boost/tuple/tuple.hpp>

// Base class for all mixins
struct Host {
    float f_;
    int i_;

    Host(float f, int i) : f_(f), i_(i) {}
};

// First mixin--constructs with 1 parameter
template <class B>
struct M1 : public B {
    char c_;

    template <class... A>
    M1(char c, const A&... a) : B(a...), c_(c) {}
};

// Second mixin--constructs with 3 parameters
template <class B>
struct M2 : public B {
    double d_;
    short s_;
    const char* p_;

    template <class... A>
    M2(boost::tuple<const char*, double, short> t, const A&... a)
    : B(a...), p_(t.get<0>()), d_(t.get<1>()), s_(t.get<2>()) {}
};


int main() {
    // ctor parameters go in this order, from most derived to base:
    M2<M1<Host>> tst(boost::make_tuple("test", 46.1, (short)-1), (char)5, 4.2f, 2);
  return 0;
}

My questions are:
1) Is there a better, more elegant way of solving this problem with C++0X?
2) Specifically, are tuples really necessary?

A: 

You could construct the base struct and pass it as constructor parameter of M1 and M2, so they can call a copy constructor of b:

M2(const B &b, ....) : B(b), ....

Are you really sure you need inheritance? Composition is more elegant and maintainable.

onof
You're right about using copy constructors as an alternative (slightly wasteful though). C++ has no built-in way to add mixins, and using CRTP as in this example is one common workaround. Attaining similar functionality to this example with composition would mean that I'd have to define D2 to have a D1-typed member, and D1 to have a Host member. It's uglier (in my opionion), but more importantly, it increases coupling. Mixins are no longer independent of each other, as decorators should be. With the current structure, I can have both M1<M2<Host>> and M1<Host>. With Composition, I cannot.
Eitan
+2  A: 

You only need something like tuples if you have multiple constructors with differing arity for the mixins (and thus ambiguities).

If not you could just handle the parameters for the mixin as usual:

template <class... A>
M2(const char* p, double d, short s, const A&... a)
  : B(a...), p_(p), d_(d), s_(s) {}
Georg Fritzsche
That's true, but I was trying to address the most general case, imposing as little restrictions on future mixins as possible.In my specific case, arity does indeed vary.
Eitan
@eit: You mean you need to have multiple constructors with varying arities in one mixin? That would be the only problematic case i can think of. Apart from that i don't see how it could be done more elegantly.
Georg Fritzsche
Maybe I wasn't clear. Each mixin only requires one constructor, but can take a different no. of args from other mixins, as in the example.
Eitan
@eit: If you only have a single constructor per mixin than there shouldn't be any problem with not using tuples. Or do you have something specific in mind?
Georg Fritzsche
You're right. It works with this simple example, but not with my actual code. I'll try to reproduce.
Eitan
Alright, I figured it out. One of my arguments was of type boost::function. Passing a function pointer to it failed for whatever reason (some intricacies of boost's automatic type deduction, I imagine). Passing the same function in object form instead (as a functor) works just fine, and I was able to discard the tuples altogether. Thanks!
Eitan