views:

272

answers:

1

Here's the scenario: I'd like to have a host class that can have a variable number of mixins (not too hard with variadic templates--see for example http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.103.144). However, I'd also like the mixins to be parameterized by the host class, so that they can refer to its public types (using the CRTP idiom). The problem arises when trying to mix the two--the correct syntax is unclear to me. For example, the following code fails to compile with g++ 4.4.1:

template <template<class> class... Mixins>
class Host : public Mixins<Host<Mixins>>... {
  public:
    template <class... Args>
    Host(Args&&... args) : Mixins<Host>(std::forward<Args>(args))... {}
};

template <class Host> struct Mix1 {};

template <class Host> struct Mix2 {};

typedef Host<Mix1, Mix2> TopHost;
TopHost *th = new TopHost(Mix1<TopHost>(), Mix2<TopHost>());

With the error:

tst.cpp: In constructor ‘Host<Mixins>::Host(Args&& ...) [with Args = Mix1<Host<Mix1, Mix2> >, Mix2<Host<Mix1, Mix2> >, Mixins = Mix1, Mix2]’:

tst.cpp:33:   instantiated from here

tst.cpp:18: error: type ‘Mix1<Host<Mix1, Mix2> >’ is not a direct base of ‘Host<Mix1, Mix2>’

tst.cpp:18: error: type ‘Mix2<Host<Mix1, Mix2> >’ is not a direct base of ‘Host<Mix1, Mix2>’

Does anyone have successful experience mixing variadic templates with CRTP?

+1  A: 

The following seems to work. I added Mixins... in the inherited mixin classes which expands the parameter pack inplace. Outside the body of Host template, all template parameters of Host must be specified so Mixins... serves the purpose. Inside the body, just Host is sufficient no need to spell out all its template parameters. Kind of a short hand.

#include <utility>

template <template<class> class... Mixins>
class Host : public Mixins<Host<Mixins...>>...
{
  public:
    Host(Mixins<Host>&&... args) : Mixins<Host>(std::forward<Mixins<Host>>(args))... {}
};

template <class Host> struct Mix1 {};
template <class Host> struct Mix2 {};

int main (void)
{
  typedef Host<Mix1, Mix2> TopHost;
  delete new TopHost(Mix1<TopHost>(), Mix2<TopHost>());
}
Sumant
Sumant
Eitan
Oh, I missed your revision. This fixes the "no matching function" error, but explodes with "internal compiler error" :) So again, which compiler are you using?
Eitan
It worked for me only on g++ 4.5. I compiled it from sources. It does not work for me on g++ 4.4.3. I got the same error you have mentioned.
Sumant
Thanks! I'll try 4.5 next.
Eitan