syntax highlight

Tuesday 18 February 2020

Mixin(ish) classes with parameter packs in C++

For some reason I couldn't find many examples of how to use a parameter pack as a mixin, to enable different features with no runtime overhead. Here is a full example of you might implement this (be aware there are some nasal daemons in the code below!). The technique is really based on this one line:

 int dummy[sizeof...(Config)] = { (Config::apply(p), 0)... };

This idiom will unpack a parameter pack and call T::apply, for each T in the parameter pack. You can use this idiom to build very clean mixin-type interfaces with static dispatch, or to build job security.

Full example:

struct EnableFeatureA {
  template <typename T> static void apply(T *a) {
    cout << a->a() << endl;
  }
};

struct EnableFeatureB {
  template <typename T> static void apply(T *a) {
    cout << T::b() << endl;
  }
};

template <typename Impl, typename... Config>
struct Foo {
  Foo(){
    // Call apply() for each type in Config
    Impl *p = nullptr;
    int dummy[sizeof...(Config)] = { (Config::apply(p), 0)... };
  }
};

struct Bar;
using FwdFoo = Foo<Bar, EnableFeatureA, EnableFeatureB>;

struct Bar : FwdFoo {
   int a() { return 4; }
   static int b() { return 2; }
};

2 comments:

  1. Keep in mind that parameter packs can be empty, in which case the array would try to have zero elements.
    Also, some apply function might return an object which overloaded the comma operator, in which case the result of the whole expression would otherwise.

    I would address the mentioned issues like this:
    int dummy[1 + sizeof...(Config)] = { 0, (static_cast(Config::apply(p)), 0)... };

    Note that nobody can override the comma operator there.

    ReplyDelete