简体   繁体   中英

Unpack vector into arguments to call functions that have a variable number of arguments

It's a little bit tricky trying to phrase this question.

So let's say I have a large number of functions that all have varying numbers of arguments - some have none, some have one, some have a few or many more. I receive the parameters for these functions in a vector. Normally, I can just call them like this:

 #include <vector>

 int my_functionA(std::string a) { /*...*/ return 1; }     
 int my_functionB(std::string a, std::string b) { /*...*/ return 2; }  

 void some_useful_function(std::vector<std::string> vec, int choice) {
      if (choice == 1) {
           // Error checking would happen here
           int val = my_functionA(vec[0]);
           // Do something with val
           // ...
      }
      else if (choice == 2) {
           // Error checking would happen here
           int val = my_functionB(vec[0], vec[1]);
           // Do something with val
           // ...
      }
 }

Error checking would be making sure there are the correct number of arguments, etc. But this gets very tedious if I have a large number of functions, and the error checks and what I do with the return value are mostly the same (ie checking that the vector size matches the number of arguments). I end up repeating very similar ~15 lines of code 100 times, and if I ever decide I want to change or add something to that 15 line 'sequence', I'd have to redo it a hundred times.

It would make more sense if I could make a map from a choice to a data structure that has the function pointer and other information I'll need, and then the my_useful_function would be more like:

 struct funcPointer {
      funcPointer(void * f, int n) : fnc(f), numOfArgs(n) {};
      void * fnc;
      int numOfArgs;
 };

 std::map<int, funcPointer> = { 
      {1, funcPointer(my_functionA, 1)},
      {2, funcPointer(my_functionB, 2)}};

 void some_useful_function(std::vector<std::string> vec, int choice) {
      if (map.count(choice) > 0) {
           // Do stuff if map[choice].numOfArgs doesn't match vec size
           int val = map[choice].fnc(vectorSomehowConvertedToArguments);
           // Do stuff with the return value
      }
 }

This answer with the 'index trick' got me really close, but since it requires a constant for the unpack_caller, I'm not sure how to mesh that into my map / data struct.

First, here's funcPointer which returns a lambda doing the std::vector -to-arguments juggling before calling a given function, generated based on the function's arity:

template <class F, std::size_t... ParamsIdx>
auto funcPointer(F f, std::index_sequence<ParamsIdx...>) {
    return [f](std::vector<std::string> const &args) {
        assert(args.size() == sizeof...(ParamsIdx));
        return f(args[ParamsIdx]...);
    };
}

template <class... Params>
auto funcPointer(int (*f)(Params...)) {
    return funcPointer(f, std::index_sequence_for<Params...>{});
}

These lambdas can then be stored together in a std::map<int, std::function<int(std::vector<std::string> const &)>> :

std::map<int, std::function<int(std::vector<std::string> const &)>> map = {
    {1, funcPointer(my_functionA)},
    {2, funcPointer(my_functionB)}
};

Finally, the call is straightforward:

void some_useful_function(std::vector<std::string> vec, int choice) {
    if (map.count(choice) > 0) {
        int val = map[choice](vec);
        // Do stuff with the return value
        std::cout << "Call succeeded, got " << val << '\n';
    }
}

See it live on Wandbox

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM