将向量解压缩为参数以调用具有可变数量参数的函数

时间:2019-03-21 13:33:07

标签: c++ c++14

试图表达这个问题有点棘手。

所以说我有很多函数,每个函数都有不同数量的参数-有些没有参数,有些只有一个,有些又有几个或更多。我通过矢量接收这些函数的参数。通常,我可以这样称呼他们:

 #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
           // ...
      }
 }

错误检查将确保有正确数量的参数,等等。但是,如果我有大量函数,这将变得非常乏味,并且错误检查和我对返回值的处理大部分相同(即检查向量大小是否与参数数量匹配)。我最终会重复非常相似的〜15行代码100次,如果我决定更改或添加一些东西到那15行“序列”中,则必须重做100次。

如果我可以将一个选择映射到一个包含函数指针和我需要的其他信息的数据结构,那将更有意义,那么my_useful_function将更像:

 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'确实使我非常接近,但是由于unpack_caller需要一个常量,因此我不确定如何将其网格化到我的地图/数据结构中。

1 个答案:

答案 0 :(得分:5)

首先,这里的funcPointer返回一个lambda,该lambda在调用给定函数之前先进行std::vector到参数的杂耍,这是根据函数的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...>{});
}

然后,这些lambda可以一起存储在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)}
};

最后,通话很简单:

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