Search code examples
c++11argument-passingstdmapstd-function

C++: Map of functions accepting any arguments and return value


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.


Solution

  • 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.