2

I would like to bind any kind of event to functions of any type for the (G)UI system of a game I am working on.

I would like to store any kind of functions and its initial arguments in a Functor template class, which I can bind to a signal binding index. Events bound to a signal binding will trigger it once they appear in the event queue, which will cause all Functors bound to this binding to be called.

I have found that the way to store the function arguments was to use an std::tuple in the functor template, but I need help initializing it correctly and unpacking it properly when calling the function.

This is what I have so far:

template<typename R, typename... Args>
class FuncBinding {
private:
    std::tuple<Args...> args;
    R(*fun)(Args...);

public:             
    FuncBinding(R(*pF)(Args...) , Args... pArgs) 
        :fun(pF), args(std::make_tuple<Args...>(pArgs)) {}

    R invoke() {
        return fun(args...);
    }

    R callFunc(Args... pArgs) {
        return fun(pArgs...);
    }
};

How do I use the parameter pack Args correctly in order to...

  • ...create the template class std::tuple<Args...> appropiately
  • ...initialize the std::tuple<Args...> instance args with the functor arguments
  • ...unpack the tuple when calling the function through the function pointer R(*fun)(Args...)
stimulate
  • 1,199
  • 1
  • 11
  • 30
  • possible [dupe](https://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer/20441189#20441189). – Walter Sep 03 '17 at 21:11

1 Answers1

3

A tuple is just another class type from the perspective of initialization, so you just initialize it by passing the pack into its constructor:

FuncBinding(R(*pF)(Args...) , Args... pArgs) 
: fun(pF), args(pArgs...)
{ }

Or, more efficiently:

args(std::forward<Args>(pArgs)...)

I'm using forward instead of std::move() in case some of Args... are lvalue reference types.


On the invocation side, there is a C++17 function called std::apply() which does what you want:

R invoke() {
    return std::apply(fun, args);
}

It is implementable in C++11. The cppreference page contains an implementation that involves std::make_index_sequence, which is itself implementable in C++11 (and there are many such implementations on this site).

Barry
  • 286,269
  • 29
  • 621
  • 977
  • thank you. I assume using the c++17 solution (`std::apply`) is generally preferred? – stimulate Sep 03 '17 at 21:15
  • @stimulate If you're using a C++17 compiler, then yes, use `std::apply()` directly - it's already written for you. If not, then you'll have to implement it yourself - but it's not hard to do so. – Barry Sep 03 '17 at 21:17
  • 1
    For those interested, a possible implementation for C++11 `index_sequence` is here: https://stackoverflow.com/a/24481400/1944004 – Henri Menke Sep 03 '17 at 21:27