C++ Delegates On Steroids

Generic version of impossibly fast delegate using C++11 variadic templates.

Posted on January 12, 2014 C++, templates, lua .

Yesterday I came across an excellent article from Jeremy Ong that introduces an extremely nice solution for integrating C++ and Lua code. Jeremy used a technique based on C++ meta-programming in conjunction with C++11 variadic templates to call Lua functions from C++ code.
That reminded me of some other example where variadic templates have been a joy to work with: coding delegates in C++.

Why Delegates?

"Don't call us, we'll call you"

Delegates enable you to pass around callable entities without introducing a too tight coupling that is usually the case when using interfaces in form of abstract base classes. They are a little like function pointers in C but in a more type-safe manner and do not only work for free functions but also for member functions. As such they are well suited e.g. for systems that communicate through events.

So why use delegates when you can also pass around objects that offer the required virtual functions in their interface? This works but introduces too many dependencies for my taste. What if you cannot change the interface of the object you are using? By using delegates instead of an explicit interface the requirements for client code are minimized. If you are implementing some kind of asynchronous service, it’s always a good idea to assume the least possible amount about your users. The result is a more functional rather then object oriented API.
Further more, no virtual functions are needed whatsoever which can be important when memory is scarce. The delegates I am talking about have a compile time interface only.

First Implementation

Before starting of I’d like to mention that the whole idea behind this delegate implementation originates from Sergey Ryazanov who wrote an excellent article about the impossibly fast c++ delegate. It is by no means complete but hopefully conveys the underlying ideas as well as the motivation for using variadic templates.
The goals for Sergey’s implementation were:

  • performance (the faster the better)
  • no dynamic memory allocation
  • compatible with the C++ Standard

Exactly the things that were also important to my usage (mainly in embedded systems programming) where dynamic memory allocation is often not possible.
The trick for a Delegate implementation is that the code that will end up calling back your delegate must not have any type dependencies to the code that implements the callback function. In Sergeys implementation a Delegate will store an untyped pointer to an object and a member-function-pointer.
Here is a first sketch for a fixed return type and a fixed parameter:

class Delegate
{
    typedef void (*Type)(void* callee, int);
public:
    Delegate(void* callee, Type function)
        : fpCallee(callee)
        , fpCallbackFunction(function) {}
    template <class T, void (T::*TMethod)(int)>
    static Delegate from_function(T* callee)
    {
        Delegate d(callee, &methodCaller<T, TMethod>);
        return d;
    }
    void operator()(int x) const
    {
        return (*fpCallbackFunction)(fpCallee, x);
    }
private:
    void* fpCallee;
    Type fpCallbackFunction;
    template <class T, void (T::*TMethod)(int)>
    static void methodCaller(void* callee, int x)
    {
        T* p = static_cast<T*>(callee);
        return (p->*TMethod)(x);
    }
};

The Delegate class provides two public functions: the static from_function is used to construct the delegate. Here the instance itself is stored along with a methodCaller function pointer that casts the untyped stored pointer back to it’s original type. The function call operator will be used to invoke the delegate.
Notice that the compiler will bake in the type information about T so that when the delegate is invoked it knows exactly about the types involved. Thus the static_cast in the methodCaller is by no means unsafe.

Checking Equality

Delegates can freely be passed around and copied since the data is only 2 pointers. Checking if one delegate corresponds to another can be easily achieved.

bool operator==(const Delegate& other) const
{
    return (fpCallee == other.fpCallee)
           && (fpCallbackFunction == other.fpCallbackFunction);
}

Usage

How can this implementation be used in real code? Say you have a class A with a function foo that you want to pass to some other code.

class A
{
public:
    void foo(int x)
    {
        printf("foo called with x=%d\n", x);
    }
    void bar(int x) {}
};
int main()
{
    A a;
    Delegate d = Delegate::from_function<A, &A::foo>(&a);
    d(42);
    printf("d==d: %s\n", d == d ? "True" : "False");
    Delegate d2 = Delegate::from_function<A, &A::bar>(&a);
    printf("d==d2: %s\n", d == d2 ? "True" : "False");
    return 0;
}

First you of course need an object of A (a). Constructing the delegate involves a call to the from_function function that takes the type of a and the member function pointer as template arguments plus a as a normal argument. Then the delegate is ready to be passed around and used.

A more Generic Version

Our first implementation has one severe drawback: It only works for a very special function signature for the delegate function.
What we would like to have is a delegate for all possible combinations of return values and argument types.

Parameter for Return Type and Argument

As a first step, let’s make the Delegate class a template so that we can vary the return type and one parameter type.

template<typename return_type, typename param_type>
class Delegate
{
    typedef return_type (*Type)(void* callee, param_type);
public:
    Delegate(void* callee, Type function)
        : fpCallee(callee)
        , fpCallbackFunction(function) {}
    template <class T, return_type (T::*TMethod)(param_type)>
    static Delegate from_function(T* callee)
    {
        Delegate d(callee, &methodCaller<T, TMethod>);
        return d;
    }
    return_type operator()(param_type x) const
    {
        return (*fpCallbackFunction)(fpCallee, x);
    }
private:
    void* fpCallee;
    Type fpCallbackFunction;
    template <class T, return_type (T::*TMethod)(param_type)>
    static return_type methodCaller(void* callee, param_type x)
    {
        T* p = static_cast<T*>(callee);
        return (p->*TMethod)(x);
    }
};

Usage

class A
{
public:
    int foo(int x)
    {
        return x*x;
    }
};
int main()
{
    A a;
    typedef Delegate<int, int> IntDelegate;
    IntDelegate d = IntDelegate::from_function<A, &A::foo>(&a);
    printf("calling delegate with return value: d(42)=%d\n", d(42));
    return 0;
}

While this is a little more flexible, we are still not at our goal for a truly generic delegate implementation.

Meet Variadic Templates

Variadic templates solve a long standing problem in C++ when working with templates. They finally allow the user to define template-classes and template-functions that can work with an arbitrarily long list of template arguments. Previously you had to write or pre-generate template specializations for multiple arguments, s.th. that for example needed to be done excessively in template-heavy boost libraries. Using variadic templates things can now be written much more smoothly.
This new feature shows up in form of parameter packs in your template definitions:

template <typename... Ts>
class A
{};
template <typename... Ts>
void foo(Ts... vs)
{}

Here, Ts is a synonym for a list of types (not a single type!) and vs stands for a list of values. To work with them you can re-expand those lists in your code.

Expansions

To help to understand what parameter packs get expanded to, it’s easiest to look at the following expansions that the compiler will take care of:

Ts... T1,…Tn
x<Ts, Y>::z... x<T1, Y>::z,… x<Tn, Y>::z
x<Ts&, Us>... x<T1&, U1>,… x<Tn&, Un>
foo(vs)... foo(v1),… foo(vn)

The ... ellipsis makes the compiler look to its left side to figure out what can be expanded. In the first case it’s just a list of types. More interesting in the second case, here Ts is expandable but not Y so the whole expression will be expanded for the Ts.
When there is more than one possibility to expand, all possible matches will be expanded together (T1 with U1, T2 with U2 and so on).
Expansion also works for lists of values when e.g. calling a function with every element in a list.

Example

Andrei had a nice little example in his talk at GoingNative 2012 that shows a simple function that makes use of variadic templates.

template <class T1, class T2>
bool isOneOf(T1&& a, T2&& b)
{
    return a == b;
}
template <class T1, class T2, class... Ts>
bool isOneOf(T1&& a, T2&& b, Ts&& ... vs)
{
    return a == b || isOneOf(a, vs...);
}
...
bool res = isOneOf(1, 2, 3.5, 4, 1, 2);

This function takes a list of arguments (at least 2) and checks if the first argument gets repeated somewhere.

Truly Generic Delegate

Now with the nice addition of variadic templates it’s possible to write a true generic version for our delegate, i.e. a delegate that is both parameterized in it’s return type and it’s argument types.

template<typename return_type, typename... params>
class Delegate
{
    typedef return_type (*Type)(void* callee, params...);
public:
    Delegate(void* callee, Type function)
        : fpCallee(callee)
        , fpCallbackFunction(function) {}
    template <class T, return_type (T::*TMethod)(params...)>
    static Delegate from_function(T* callee)
    {
        Delegate d(callee, &methodCaller<T, TMethod>);
        return d;
    }
    return_type operator()(params... xs) const
    {
        return (*fpCallbackFunction)(fpCallee, xs...);
    }
private:
    void* fpCallee;
    Type fpCallbackFunction;
    template <class T, return_type (T::*TMethod)(params...)>
    static return_type methodCaller(void* callee, params... xs)
    {
        T* p = static_cast<T*>(callee);
        return (p->*TMethod)(xs...);
    }
};

Usage

class A
{
public:
    int foo(int x)
    {
        return x*x;
    }
    int bar(int x, int y, char a)
    {
        return x*y;
    }
};
int main()
{
    A a;
    auto d = Delegate<int, int>::from_function<A, &A::foo>(&a);
    auto d2 = Delegate<int, int, int, char>::from_function<A, &A::bar>(&a);
    printf("delegate with return value: d(42)=%d\n", d(42));
    printf("for d2: d2(42, 2, 'a')=%d\n", d2(42, 2, 'a'));
    return 0;
}

Finally we have Delegate available for all the function types we want to use on our delegate objects. Ok, the syntax might be a little verbose but that can be amended somewhat using a factory function for our delegates in combination with a little C macro.

Syntactic Sugar please!

Let’s see what we can do to make delegates a little more pleasing to the eye:

template<typename return_type, typename... params>
class Delegate
{
    ...
};
template<typename T, typename return_type, typename... params>
struct DelegateMaker
{
    template<return_type (T::*foo)(params...)>
    static return_type methodCaller(void* o, params... xs)
    {
        return (static_cast<T*>(o)->*foo)(xs...);
    }
    template<return_type (T::*foo)(params...)>
    inline static Delegate<return_type, params...> Bind(T* o)
    {
        return Delegate<return_type,
               params...>(o, &DelegateMaker::methodCaller<foo>);
    }
};
template<typename T, typename return_type, typename... params>
DelegateMaker<T, return_type, params... >
makeDelegate(return_type (T::*)(params...))
{
    return DelegateMaker<T, return_type, params...>();
}
#define DELEGATE(foo, thisPrt) (makeDelegate(foo).Bind<foo>(thisPrt))

Puuhh…looks worse than it is since this is only library code that users of our delegate implementation will never need to look at. The DelegateMaker together with the makeDelegate function is needed to make use of C++’s template argument deduction which unfortunately only works on template functions and not class templates. But now we can easily have our cake and eat it, too.

Usage

class A
{
public:
    void deadSimple()
    {
        printf("no params whatsoever\n");
    }
    int foo(int x)
    {
        return x*x;
    }
    int bar(int x, int y, char a)
    {
        return a == 'a' ? x+y : x*y;
    }
    void crazy(int I, char wanna, float go, const char* crazy)
    {
        printf("I=%d, wanna=%c, go=%f, crazy=%s\n",
               I, wanna, go, crazy);
    }
};
int main()
{
    A a;
    auto d = DELEGATE(&A::foo, &a);
    auto d2 = DELEGATE(&A::bar, &a);
    auto d3 = DELEGATE(&A::crazy, &a);
    auto d4 = DELEGATE(&A::deadSimple, &a);
    printf("d(42)=%d\n", d(42));
    printf("d2(42, 2, 'a')=%d\n", d2(42, 2, 'a'));
    const char* s = "sheeeet!";
    d3(5, 'a', 4.5, s);
    d4();
    return 0;
}

There you go. An extremely fast delegate implementation that looks quite usable to my eyes thanks to C++11’s variadic templates.


If you are interested, the source-code is available on github.