0

I'm trying to create a variadic template class that looks like this :

template <class... Args>
class Message
{
    private:

        std::function<void(Args...)> _function;
    public:
    //stuff

};

I want to have an option to bind so the user can bind any function (with corresponding arguments) when he wants. I've tried this approach (this function is a part of the Message class, so Args... comes from the template argument of the class):

    template<class T>
    void Message::bindFunction(void(T::*function)(Args... args), T* ownerPtr)
    {
        //This doesn't work
        _function = boost::bind(function,args, ownerPtr);
    }

However the above doesn't work, I've tried to create the tuple from the args in the bind function, but the compiler keeps saying:

<args_0> undeclared identifier.

(If I pass more arguments it changes)

From the user's point of view it should look like this:

class Foo
{
public:

    void test2(int a, float b, double c) 
    {  
        std::cout << "bla";
    }
};

int main()
{
    Foo f;

    Message<int,float,double> Mess;

    Mess.bindFunction(&Foo::test2, &f);
}

I've seen a lot of articles talking about forwarding the parameters, however most of the answers confuse me more than I am already with different template structures without explaining them.

AndyG
  • 39,700
  • 8
  • 109
  • 143
Pins
  • 217
  • 1
  • 12
  • 4
    You're already using C++11; a lambda would be more straightforward than `boost::bind` (or `std::bind`, which also exists). – chris Nov 16 '17 at 18:15
  • @chris I know about the existence of the std::bind, the use of boost::bind was inteded as I wanted to create functors to member functions. – Pins Nov 16 '17 at 18:44
  • 1
    @Pins, `std::bind` also works for member functions. In fact, it's one of the examples on [cppreference](http://en.cppreference.com/w/cpp/utility/functional/bind). Regardless, hooking up variadics to either is painful at best unless [`std::bind_front`](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0356r2.html) makes it through. There's a [C++ Weekly](https://www.youtube.com/watch?v=ZlHi8txU4aQ) video on avoiding `bind` with lambdas, but it sounds like some extra variadic template material might be beneficial. – chris Nov 16 '17 at 18:51
  • @chris In fact i was using std::bind before, however had some troubles with it when experimenting with linux platform (cannot remember the issue), tried the boost::bind and then decided to stick with it. Will check the bind_front. – Pins Nov 16 '17 at 18:53
  • @chris `std::bind_front` successfully made it through LEWG (Library Evolution Working Group) last week in Albuquerque. – Nevin Nov 16 '17 at 20:31
  • @chris didn´t notice your note about lambdas and bind, thanks for that and also for this helpful video. – Pins Nov 16 '17 at 20:43

1 Answers1

2

Use a lambda because they're much nicer (Live Demo (C++11)):

template<class T>
void bindFunction(void(T::*function)(Args...), T* ownerPtr)
{
    _function = [function, ownerPtr](Args... args)
    {
        (ownerPtr->*function)(args...);
    };
}

We are capturing the function pointer and pointer to instance of the class with [function, ownerPtr] in the lambda declaration.

The lambda accepts a list of arguments whose types are specified in the variadic template for the Message class: Args... args.

Internally we call the function using our instance with the (ownerPtr->*function) syntax, and then pass in the arguments by expanding the parameter pack (args...)

To call the function you might write something like this:

void callFunction(Args&&... args)
{
    _function(std::forward<Args>(args)...);
}

We use perfect forwarding to pass our arguments to the std::function<void(Args...)> member.

AndyG
  • 39,700
  • 8
  • 109
  • 143
  • I´m surprised how handily the lambda works here, now it works great, thanks. – Pins Nov 16 '17 at 19:18
  • 2
    @Pins: It would have been exceptionally difficult with `bind` because a variadic placeholder does not exist [so you'd have to write your own](https://stackoverflow.com/a/21193316/27678) – AndyG Nov 16 '17 at 19:22