How to pass a function pointer when the pointee has optional parameters?

Viewed 278

I'm trying to pass a function pointer as a parameter of another function, but the function pointer may or may not itself have an argument (making it different from the other questions I searched for).

The code works as is, but MY problem is that I was trying to use a single function and pass in each different function pointer, but what I have below is 3 different functions to pass each function pointer. I'd like to get rid of the 3 different function definitions as they are all the same with the exception of the passed in function pointer (so basically, 3 copies of execute_func() definitions). Here's what I have so far but this doesn't seem right that I should need three execute_func() calls.

class A { ... };
class B { ... };

class Test {
    private:
        std::function<void()> fp;
        std::function<void(MyA &)> fp;
        std::function<void(MyB &)> fp;
    // ...
};

// Here I create a function pointer for each of my calls.
Test::Test() {
    fp = std::bind(&Test::do_this, this);
    fp_a = std::bind(&Test::do_a, this, std::placeholders::_1);
    fp_b = std::bind(&Test::do_b, this, std::placeholders::_1);
}

// Here my intention was to have only 1 execute_func() call and I would 
// pass in the pointer to the function that I want to call.
Test::test_it()
{
    A a;
    B b;

    execute_func(fp);
    execute_func(fp_a, a);
    execute_func(fp_b, b);
}

// I was hoping to only need one function, but so far
// have needed 3 functions with diff signatures to make it work.
bool Test::execute_func(std::function<void()> fp) {
    // ... more code here before the fp call
    fp();
    // ... them more common code here.
}

bool Test::execute_func(std::function<void(MyA &)> fp, MyA &a) { 
    // ... more common code here 
    fp(a);
    // ... and more common code here
}

bool Test::execute_func(std::function<void(MyB &)> fp, MyB &b) {  
    // ... more common code here 
    fp(b);
    // ... and more common code here.
}

// And of course the execute_func() calls call these members as passed in.
bool Test::do_this() { ... }
bool Test::do_a(MyA &a) { ... }
bool Test::do_b(MyB &b) { ... }

Thoughts on where I'm going wrong?

1 Answers

To do this you can use a variadic template.

template<typename Ret, typename... Args>
bool Test::execute_func(Ret fp(Args&&...), Args&&... args)
{
    // do stuff
    fp(args...);
    // do more stuff
}

I'm using an ordinary function pointer here instead of std::function, which is, IMO, baroque and wholly unnecessary (it's a relic from when C++ didn't have lambdas). But it's the same idea.

template<typename Ret, typename... Args>
bool Test::execute_func(const std::function<Ret(Args&&...)> &fp, Args&&... args) { }

Edit: Jarod42 points out that you can make it so it will work with both function pointers, std::function and any other callable object as follows:

template<typename Func, typename... Args>
bool Test::execute_func(Func &&fp, Args&&... args)
{
    // do stuff
    fp(args...);
    // do more stuff
}

To make it even more generic you can use std::invoke to call fp instead of directly calling it, which additionally allows fp to be a member function or data member (with the instance pointer given in the first subsequent argument).

template<typename Func, typename... Args>
bool Test::execute_func(Func &&fp, Args&&... args)
{
    // do stuff
    std::invoke(fp, args...);
    // do more stuff
}

The previous example could also equivalently be written

template<typename... InvokeArgs>
bool Test::execute_func(InvokeArgs&&... iargs)
{
    // do stuff
    std::invoke(iargs...);
    // do more stuff
}
Related