views:

141

answers:

4

I have a class whose functionality I'd like to depend on a set of plug-in policies. But, I'm not sure how to get a class to derive from an arbitrary number of classes.

The code below is an example of what I'm trying to achieve.

// insert clever boost or template trickery here
template< class ListOfPolicies >
class CMyClass : public ListOfPolicies 
{
public:
    CMyClass()
    {
        // identifiers should be the result of OR-ing all 
        // of the MY_IDENTIFIERS in the TypeList.
        DWORD identifiers; 

        DoSomeInitialization( ..., identifiers, ... );
    }

    int MyFunction()
    {
        return 100;
    }

    // ...
};

template< class T >
class PolicyA
{
public:
    enum { MY_IDENTIFIER = 0x00000001 };

    int DoSomethingA()
    {
        T* pT = static_cast< T* >( this );
        return pT->MyFunction() + 1;
    };

    // ...
};

template< class T >
class PolicyB
{
public:
    enum { MY_IDENTIFIER = 0x00000010 };

    int DoSomethingB()
    {
        T* pT = static_cast< T* >( this );
        return pT->MyFunction() + 2;
    };

    // ...
};

int _tmain(int argc, _TCHAR* argv[])
{
    CMyClass< PolicyA > A;
    assert( A.DoSomethingA() == 101 );

    CMyClass< PolicyA, PolicyB > AB
    assert( AB.DoSomethingA() == 101 );
    assert( AB.DoSomethingB() == 102 );

    return 0;
}

Thanks, PaulH

+4  A: 

For deriving from an arbitrary list of types, you'll have to list an arbitrary number of types. For that, I only know type lists. I'll use this for the following code:

class nil {};

template< typename H, class T >
struct type_list {
    typedef H head_type;
    typedef T tail_type;
};

Given that, you can create a template that derives from all the types in a type list:

template< class TL >
struct derivator;

template<>
struct derivator<nil> {};

template< typename H, typename T >
struct derivator< type_list<H,T> > : public H
                                   , public derivator<T> {};

I tested this with the following code

class A {};   void a(A) {std::cout << "A\n";}
class B {};   void b(B) {std::cout << "B\n";}
class C {};   void c(C) {std::cout << "C\n";}

class X : public derivator< type_list<A 
                          , type_list<B 
                          , type_list<C 
                          , nil > > > > {};

int main(void)
{
    X x;
    a(x);
    b(x);
    c(x);

    return 0;
}

Both VC and Comeau seem to like this code and it prints

A
B
C

so it seems to work.

sbi
I didn't mean to suggest I was married to the concept of typelists by the comment at the top of the code. I'm open to anything.I'll assume these definitions for your example: class CNil {}; template< class T, class U > struct type_list { typedef T THead; typedef U TTail; };I still don't see how I actually use this, though. If I do: void _tmain(int argc, _TCHAR* argv[]) { CMyClass< type_list< PolicyA, PolicyB > > AB; } Then I get an "error C2504: 'derivator<TList>' : base class undefined" on your "struct derivator<...>" definition.
PaulH
@PaulH: I don't see any other way than type lists for listing an arbitrary number of types. `:)` The code I pasted was just something to get you going and I didn't know whether it would compile. I'll check this and replace it by something that compiles in a few minutes.
sbi
+1, this is what I was going to write. I believe this is the pattern that Andrei Alexandrescu put forward in Modern C++ Design as Loki::GenScatterHierarchy<>.
Kaz Dragon
@Kaz: I might have learned this from him. I read the books many years ago and I don't have it here, so I don't know if it uses the same technique.
sbi
+6  A: 

With the Boost.MPL library:

//Warning: Untested
namespace bmpl = boost::mpl;
template<class Typelist>
class X : bmpl::inherit_linearly<Typelist, bmpl::inherit<bmpl::_1, bmpl::_2> >::type
{
...
};

Used as:

X<bmpl::vector<Foo, Bar, Baz> > FooBarBaz;

For the "OR-ing all MY_IDENTIFIER" part, something along the lines of the following:

//Warning: still not tested:
enum {OR_ED_IDENTIFIERS = 
    bmpl::fold<Typelist, bmpl::int_<0>, bmpl::bitor_<_1, _2> >::value;
}
Éric Malenfant
That works until I create a bmpl::vector<> of the two policies in the example above. They're both templates, so I get errors like: "error C3203: 'PolicyA' : unspecialized class template can't be used as a template argument for template parameter 'T0', expected a real type"
PaulH
You couldn't inherit directly from a single policy template anyway, a base class always needs to be a class. Not a built-in type, not a function, nor a template. An instantiation of a class template is a class, so that would be acceptable.
MSalters
@PaulH: I missed the fact that, in your post, policies were not classes, but templates. As MSalters comments, you can't have a class inheriting from a template.
Éric Malenfant
Then that sounds like my answer. Thanks all.
PaulH
A: 

A possible implementation using type lists:

#include <cstdlib>

/**
 * Base classes that you want to inherit from.
 */
struct A
{
    A() {std::cout << "A()" << std::endl;}
};

struct B
{
    B() {std::cout << "B()" << std::endl;}
};

struct C
{
    C() {std::cout << "C()" << std::endl;}
};

/**
 * Typelists
 */
struct NullType {};

template <typename H, typename T>
struct InheritanceList : public H, public T
{
    typedef H head_t;
    typedef T tail_t;
};

template <typename H>
struct InheritanceList<H,NullType> : public H
{
    typedef H        head_t;
    typedef NullType tail_t;
};

#define INHERITANCE_LIST_1(x)     InheritanceList<x,NullType>
#define INHERITANCE_LIST_2(x,y)   InheritanceList<x,InheritanceList<y,NullType>
#define INHERITANCE_LIST_3(x,y,z) InheritanceList<x,InheritanceList<y,InheritanceList<z,NullType> > >

/**
 * Your class
 */
struct MyClass : public INHERITANCE_LIST_3(A,B,C)
{
};

/**
 * Entry point
 */
int main( int argc, char** argv )
{
    MyClass mc;

    return EXIT_SUCCESS;
}
Aurélien Vallée
Aurélien, those macros aren't really necessary. You can do exactly this with a template, too.
sbi
True! I did not even thought of using templates, I'm used to these macros since that's what's in Alexandrescu's book. Thanks for the idea :)
Aurélien Vallée
Yes, I think he once said he only discovered the idea with default template parameters afterwards.
sbi
A: 

I would not recommend to use inheritance for a plugin like architecture, with time your classes will get very big, bloated and static. Use instead composition, for example have a vector of plugins in your class.

fritzone
_Public_ inheritance is quite different from composition. Also, AFAIK this idiom is called "mix-in classes". So this i'is common enough to have a name. If all those mix-ins add is overridden virtual functions, the classes won't grow even a byte, because all this does is change function pointers in the vtable.
sbi