11

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?

Eitan
  • 862
  • 1
  • 7
  • 17

1 Answers1

8

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
  • 4,286
  • 1
  • 23
  • 31
  • I revised it to include templatized constructor. template Host(Args&&... args) : Mixins(std::forward>(args))... {} – Sumant Mar 17 '10 at 15:34
  • Thanks, Sumant. Your suggestion makes sense, but fails to work somehow for me. Which compiler version are you using? I've copied-and-pasted this code and when compiling, got: tst2.cpp: In function ‘int main()’: tst2.cpp:16: error: no matching function for call to ‘Host::Host(Mix1 >, Mix2 >)’ tst2.cpp:7: note: candidates are: Host::Host(Mixins >&& ...) [with Mixins = Mix1, Mix2] tst2.cpp:5: note: Host::Host(const Host&) – Eitan Mar 17 '10 at 15:44
  • 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 Mar 17 '10 at 15:50
  • 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 Mar 17 '10 at 15:51
  • 1
    How would one make it so that the passed in mixins have access to the Host members too, not just the types? I'd assume the only way to make it so is to have mixins hold a pointer/reference to the Host class. This is easy if we assume the Mixins all have the same constructor `Mix(Host &)`, but what if, like in the example given above, every mixins constructor can have its own set of parameters? – Fabio A. Feb 01 '17 at 10:44
  • Answering my own question. Turns out that one can use `static_cast(*this)` from within any mixins method to get access to the Host instance. Correct me if I'm wrong, a downcast done with `static_cast` is a well defined idiom in the context of the CRTP pattern. – Fabio A. Feb 01 '17 at 11:37