Is it possible, and how, can I have a std::map
of functions that can take any number of arguments and return any type that can be converted to a std::string
?
What I have tried was using std::function
but when declaring the std::map
I still always had to specify the type of the arguments that the function can allow. e.g. std::map<int,std::function<
INT, BOOL>>
.
I have also tried multiple variations of variadic functions and derived classes but none seemed to have given me what I need.
I've also seen some lambda examples that I didn't quite understand but still not sure if that was the right approach.
In principle what I am trying to achieve is to have a function of any type or form bound to each key that can be pressed on the keyboard. See image of how I imagined this map visually would look like: link to image When pressing a key, the function corresponding with the colour of the key, must be called.
You may have one or more piece of code that call these functions depend on the key stroke:
if (key_pressed == 'q') {
f3(some_boolean_variable);
} else if (key_pressed == 'f') {
f4(some_string_variable);
}
Even you have some way to use a map from key to function, you can't specify how these variables are picked and passed in to the chosen function without enumerating all functions (aka, use a map).
My suggestions:
1) Use lambda capture and std::bind
for what you already have when generating (not calling) these functions.
std::map<char, std::function<void(int)>> m;
m['m'] = [](int) {...};
string s = ... // suppose you already know what s should be.
m['i'] = [s](int) {...};
2) Try to redesign to use the same signature, e.g. std::function<void(const ContextData&)>
using std::experimental::optional;
using std::tuple;
struct ContextData {
optional<bool> bool_value;
optional<tuple<int, string>> f2_args;
optional<tuple<int, string, bool, float, int, int, int>> f5_args;
};
Notice that above may solve your problem in a crappy way, but it's not a good design. Use this "a bunch of optional values" pattern only if it looks clean, is easy to understand and makes sense.
3) If you can't come up with a clean ContextData, use boost::variant
or even boost::any
. Use them wisely.
std::map<char, std::function<void(boost::any)>> m;
using std::tuple;
using std::tie;
using boost::any;
using boost::any_cast;
m['i'] = [](any data) {
int i;
std::string s;
// May throw.
tie(i, s) = any_cast<tuple<bool, std::string>>(data);
// use i and s
};
If I were you, I will do a redesign to make the parameter lists consistent. It's not just for making compiler happy though; it's more like organizing the logic in a rational, non-ad-hoc way.