Search code examples
c++templatesboostmetaprogrammingboost-mpl

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.


Solution

  • 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.