Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I invoke a non-default constructor for each inherited type from a type list?

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.

like image 851
Steve Guidi Avatar asked Mar 01 '23 02:03

Steve Guidi


2 Answers

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.

like image 175
Matthieu M. Avatar answered Apr 08 '23 06:04

Matthieu M.


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.

like image 38
Éric Malenfant Avatar answered Apr 08 '23 07:04

Éric Malenfant