Vector of std::function with different signatures

ksl picture ksl · Oct 2, 2014 · Viewed 11.9k times · Source

I have a number of callback functions with different signatures. Ideally, I would like to put these in a vector and call the appropriate one depending on certain conditions.

e.g.

void func1(const std::string& value);

void func2(const std::string& value, int min, int max);

const std::vector<std::function<void(std::string)>> functions
{
    func1,
    func2,
};

I realise the above isn't possible, but I wonder if there are any alternatives I should consider. I haven't been able to find any yet, and I've experimented with std::bind but not managed to achieve what I want.

Is such a thing possible?

Answer

Jonathan Wakely picture Jonathan Wakely · Oct 2, 2014

You haven't said what you expect to be able to do with func2 after putting it in a vector with the wrong type.

You can easily use std::bind to put it in the vector if you know the arguments ahead of time:

const std::vector<std::function<void(std::string)>> functions
{
    func1,
    std::bind(func2, std::placeholders::_1, 5, 6)
};

Now functions[1]("foo") will call func2("foo", 5, 6), and will pass 5 and 6 to func2 every time.

Here's the same thing using a lambda instead of std::bind

const std::vector<std::function<void(std::string)>> functions
{
    func1,
    [=](const std::string& s){ func2(s, func2_arg1, func2_arg2); }
};

If you don't know the arguments yet, you can bind references to some variables:

int func2_arg1 = 5;
int func2_arg2 = 6;
const std::vector<std::function<void(std::string)>> functions
{
    func1,
    std::bind(func2, std::placeholders::_1, std::ref(func2_arg1), std::ref(func2_arg2))
};

Now functions[1]("foo") will call func2("foo", func2_arg1, func2_arg2), and you can assign new values to the integers to pass different arguments to func2.

And using a lambda function instead of std::bind

const std::vector<std::function<void(std::string)>> functions
{
    func1,
    [&](const std::string& s){ func2(s, func2_arg1, func2_arg2); }
};

This is pretty ugly though, as you need to keep the int variables around for as long as the callable object (the closure or the bind expression) referring to them exists.