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?
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:Now
functions[1]("foo")
will callfunc2("foo", 5, 6)
, and will pass5
and6
tofunc2
every time.Here's the same thing using a lambda instead of
std::bind
If you don't know the arguments yet, you can bind references to some variables:
Now
functions[1]("foo")
will callfunc2("foo", func2_arg1, func2_arg2)
, and you can assign new values to the integers to pass different arguments tofunc2
.And using a lambda function instead of
std::bind
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.Not sure how useful this would be for you, it is based on
boost::any
, redundant parameters are ignored. You can addtry...catch
forboost::bad_any_cast
to prevent crash in case of mismatch between arguments' and parameters' types. Though I think regularstd::bind
is a better choice.DEMO
What you want is possible through
polymorphism
. The idea is to create a class with a specific signature, which at runtime will call different methods. For example:You can make the derived classes as complicated as you need be, but since in the end they all have the same interface you will be able to call all of them.
Direct answer to your question is "NO". Any runtime container would only let you store objects of the same type and std::function<> instantiated with different signatures will be different data types.
Generally the reason you may want to have "a vector of functions with different signatures" is when you have something like the below (three step processing where input interface is unified (
buffer& buf
and output interface is unifiedon_event(Event evt)
), but the layer in the middle is heterogeneousprocess_...(...)
In a solution not involving metaprogramming you'd typically pre-cook a functor doing the end-to-end at initialization time and store those in the vector:
If you've got an int and a string, you cannot put them in one vector but you can put them in one struct or
std::tuple<>
. The same applies for two function types.std::function
erases the exact type of the function object but preserves the function call signature. If you cannotbind
the extra arguments in advance as Jonathan Wakely recommends, you can use aboost::variant< std::function<...>, std::function<...> >
as your vector member. At the call site you can then check if the vector contains the right kind of function object and call it accordingly.