Game Development by Sean

C++ Runtime Invocation of Bound Function via Variadic Templates

Table of Contents

A fellow traveler in the land of C++ was attempting to perform binding of a function using variadic templates and invoking it at runtime with an array of “variant” data types. Several of our companions asserted that this was impossible with C++11. Not quite believing them, I set out to solve the problem. The following is a very simple demo made to show how this can be done.

Forward

The code sample does not try to do anything fancy. It doesn’t register bound function. It doesn’t have a type system. It has no fancy variant system, instead just using void* and trusting you to know what you’re doing. It doesn’t deal with member functions. It doesn’t try to encapsulate a return value at all. All of these should be straight-forward to implement for anyone who’s done C++ reflection system coding before. The semi-novel piece is doing this with variadic templates.

Supported Compilers

The only major new C++11 feature required is variadic template support. GCC 4.3, Clang 2.9, or Visual Studio 2012 + November ‘12 CTP should all work. This has been tested with GCC 4.7, GCC 4.8, and Clang 3.0. Visual Studio may have some trouble for larger numbers of arguments as std::tuple is not updated to use variadic templates in the November ‘12 CTP.

Recursion

The primary trick relies in recursively unpacking arguments from the variant array (a void** in the demo) and appending each argument to a list of argument for the next call. The demo does not attempt to ensure there’s any perfect forwarding going on, so some application of std::forward may be appropriate. Simply adding it will require another overload for the first apply call, since std::forward(ArgsT…)(args…) is only valid if args is a non-empty parameter pack.

Each call to apply::call unpacks one more argument from the variant array and then calls itself, keeping track of how many elements remain to unpack (which is the arity of the function being invoked). When there are zero arguments left, a terminator to the recursion, which is implemented with template specialization on a non-type template parameter, invokes the function with the bound arguments.

std::tuple

There is no standard utility function to get the Nth type in a parameter pack. However, there is a utility function to get the Nth type from a tuple type. We use construct a tuple type from the variadic type and then use this utility to extract the Nth type. A custom utility could be written which does not depend on std::tuple.

The Code

// Public Domain. 2013, Sean Middleditch, <sean@seanmiddleditch.com> #include #include

template struct apply { template <typename Ret, typename... Args, typename... ArgsT> static Ret call(Ret(*func)(Args...), void** v, ArgsT... args) { return apply::call(func, v, args..., *static_cast<typename std::tuple_element<sizeof...(args), std::tuple>::type*>(v[sizeof...(ArgsT)])); } };

template<> struct apply<0> { template <typename Ret, typename… Args, typename… ArgsT> static Ret call(Ret(*func)(Args…), void** v, ArgsT… args) { return func(args…); } };

template <typename Ret, typename… Args> static Ret call(Ret(*func)(Args…), void** v) { return apply<sizeof…(Args)>::call(func, v); }

static int test(int a, int b, float c) { return a + b * (int)c; }

int main(int argc, char** argv) { int a = 3, b = 4; float c = 5; void* v[3] = { &a, &b, &c }; std::cout « call(test, v) « std::endl; return 0; } </code></div>