views:

288

answers:

4

I'm using a boost typelist to implement the policy pattern in the following manner.

using namespace boost::mpl;

template <typename PolicyTypeList = boost::mpl::vector<> >
class Host : public inherit_linearly<PolicyTypeList, inherit<_1, _2> >::type
{
public:
    Host() : m_expensiveType(/* ... */) { }

private:
    const ExpensiveType m_expensiveType;
};

The Host class knows how to create an instance of ExpensiveType, which is a costly operation, and each policy class exposes functionality to use it. A policy class will always minimally have the constructor defined in the following sample policy.

struct SamplePolicy
{
    SamplePolicy(const ExpensiveType& expensiveType)
        : m_expensiveType(expensiveType) { }

    void DoSomething()
    {
        m_expensiveType.f();
        // ...
    }

private:
    const ExpensiveType& m_expensiveType;
};

Is it possible to define the constructor of Host in such a way to call the constructor of each given policy? If the type list was not involved, this is very easy since the type of each policy is explicitly known.

template <typename PolicyA, typename PolicyB>
class Host : public PolicyA, public PolicyB
{
public:
    Host() :
        m_expensiveType(/* ... */),
        PolicyA(m_expensiveType),
        PolicyB(m_expensiveType) { }

private:
    const ExpensiveType m_expensiveType;
};

The boost::mpl::for_each algorithm looks promising, but I can't wrap my head around how to use it to solve this problem.

+3  A: 

As mentioned in the comment, you need to chain the constructor calls. For that, every type in the derivation chain has to know what type its derived from - to allow for arbitrary derivation sequences we need to make that types templates so their base can be any type.
This allows us to refer to the base and explicitly call its constructors.

I scetched out a basic example, but did end up not using boost because mpl::vector expects known types and i needed to hand it template template parameters. Instead i used a custom typelist that supports template template parameters and implicitly derives.

struct expensive {};

// derivation list

struct nil {}; // list end
struct Noop {  // do nothing on end of derivation chain
    Noop(expensive& e) {}
};

template<template <typename T> class H, typename L>
struct DL {
    typedef L tail;
    typedef H<typename tail::head> head;
};

template<template <typename T> class H>
struct DL<H, nil> {
    typedef H<Noop> head;
};

// example types

template<class T>
struct A : T {
    A(expensive& e) : T(e) {}
};

template<class T>
struct B : T {
    B(expensive& e) : T(e) {}
};

// derivation chain usage example

typedef DL<A, DL<B, nil> > DerivationChain;

class User : DerivationChain::head
{
public:
    User(expensive& e) : DerivationChain::head(e) {}
};

int main(int argc, char** argv)
{
    expensive e;
    User u(e);
}
Georg Fritzsche
The idea is simple and efficient, though the use of head seems a bit awkward. Not really polished imho.
Matthieu M.
I know, just a quick approach to get the idea across.
Georg Fritzsche
A: 

Create a parameterised constructor and pass pameters to it. In this way you could acheive two things simultaneouly. 1) Constructor overloading 2) Avoid call to default constructor.

Sachin Chourasiya
The question is not about basic constructor usage.
Georg Fritzsche
+3  A: 

If you want this kind of generation, I can only recommend a read of Alexandrescu's Modern C++ Design. There is an entire chapter dedicated to the generation of hierarchy from a typelist. You can also find it on Loki's website: Hierarchy Generators; though you will miss the diagrams and explanations, as well as the process.

For you particular problem, this seems pretty straightforward.

// Helper
struct nil
{
};

template < class Head, class Tail = nil>
struct SH: Head<Tail> /* for SimpleHierarchy */
{
  SH(const ExpensiveType& e): Head(e), SH<Tail>(e) {}
};

template<>
struct SH<nil,nil>
{
  SH(const ExpensiveType& e) {}
}:

// Policies
class A
{
public:
  A(const ExpensiveType& e) : T(e), m_e(e) {}

private:
  const ExpensiveType& m_e;
};

class B
{
public:
  B(const ExpensiveType& e) : T(e), m_e(e) {}

private:
  const ExpensiveType& m_e;
};

class C
{
public:
  C(const ExpensiveType& e) : T(e), m_e(e) {}

private:
  const ExpensiveType& m_e;
};

// Use
// nesting example
typedef SH<A, SH<B,C> > SimpleHierarchy;

// Your example, revisited
template <class A, class B>
class Host: SH<A,B>
{
public:
  Host(const ExpensiveType& e): SH<A,B>(e), m_e(e) {}

private:
  const ExpensiveType& m_e;
};

Of course, this is a sketch only. The main problem here is the extensibility. If you read Alexandrescu's book, you'll learn much more, and if you don't have the time, do take a peak at the source code, that might prove just what you need.

There are ways to do it directly from the mpl::vector, the only thing to realize is that you cannot do this with a big MI single-layer, but you can add many layers.

Here, I chose not to add complexity at the Policy level (they are not templatized) and to rely on MI (dual) at each level instead. You could make it purely linear, but templatizing your policies means that you cannot define them in a source file.

Also note that this approach can be adapted to take a mpl::vector directly, but this would involve the use of meta-template programming operations: back, pop_back and empty at the very least, which might obfuscate the code more than they actually help.

Matthieu M.
The dual MI approach definitely makes it cleaner as it does not mix the derivation semantics into the policies.
Georg Fritzsche
Looks like `struct SH: Head<Tail>` should be `struct SH : Head, SH<Tail>`?
Georg Fritzsche
+2  A: 

I could not resist the temptation to see how it could be done with inherit_linearly. Turns out to be not that bad, IMHO:

template<class Base, class Self>
struct PolicyWrapper : Base, Self
{
    PolicyWrapper(const ExpensiveType& E)
        : Base(E), Self(E)
    {}
};

struct EmptyWrapper
{
    EmptyWrapper(const ExpensiveType& E)
    {}
};

template <typename PolicyTypeList = boost::mpl::vector<> >
class Host : 
    public inherit_linearly<
       PolicyTypeList, 
       PolicyWrapper<_1, _2>, 
       EmptyWrapper
    >::type
{

typedef typename inherit_linearly<
    PolicyTypeList, 
    PolicyWrapper<_1, _2>, 
    EmptyWrapper
>::type BaseType;

public:
    Host() : BaseType(m_expensiveType)
    {}

private:
    const ExpensiveType m_expensiveType;
};

A warning though: Passing a reference to an uninitialized member like what is done in the Host ctor is very fragile. If, for example, one writes a Policy like this:

struct BadPolicy
{
    BadPolicy(const ExpensiveType& E)
    : m_expensiveType(E)
    {}

    ExpensiveType m_expensiveType;
};

bad things will happen, as the copy ctor of ExpensiveType will be invoked with an uninitialized object.

Éric Malenfant
An excellent point on invoking the copy ctor of an uninitialized type. Best to be safe and pass a pointer instead.
Steve Guidi
@Steve: Well, passing a pointer to an uninitialized type is not much different. It may be a stronger indication to the receiver that the object has reference (and not value) semantics and prevent the kind of errors I'm showing in my answer but, in practice, the receiver of the pointer can do anything with the pointed-to object, just like with the reference.
Éric Malenfant
As someone popping in just learning this stuff, I would *really* like you explain why this works. It might be really simple, but then it's just a bit of description.
Narfanator
@Narfanator: I'm not sure what you're actually looking for, but let me try. Please tell me if that answers the question: `inherit_linearly<Types, Node, Root>::type` can be seen as analogous to `std::accumulate`: It takes a sequence (Types) and an initial value (Root) and returns the result of successively applying a binary "function" (Node) on each element in the sequence and on the accumulated result from the previous invocations.(continued on next comment)...
Éric Malenfant
... (continuing from previous comment)To keep thigs simple, consider a 3 elements sequence, `seq`. `std::accumulate(seq.begin(), seq.end(), init, fun)` returns `fun(fun(fun(init, i0), i1), i2)` (where `iN` denotes the value of the `N`th element in `seq`). `inherit_linearly` is similar, but it computes types instead of values, operates at compile time instead of at runtime, and is "invoked" by looking at the nested "type" typedef.`inherit_linearly<vector<A, B, C>, Fun<_1, _2>, Empty>::type` is thus: `Fun<Fun<Fun<Empty, A>, B>, C>`.(continued on next comment)...
Éric Malenfant
...(continuing from previous comment) Note that the resulting type does not directly inherits from A, B and C. It inherits from C, and from a type wich, in turns, inherits from B and from a type inheriting from A and Empty.Now, the trick to achieve what the OP asks, is to define "Fun<X, Y>" so that it inherits from both X and Y, and have its ctor initialize its X and Y bases as desired. This is what PolicyWrapper does.
Éric Malenfant
Perfect! Thanks.
Narfanator