首页 > 解决方案 > 将向量解包到参数中以调用具有可变数量参数的函数

问题描述

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

所以假设我有大量的函数,它们都有不同数量的参数——有些没有,有些只有一个,有些有几个或更多。我在向量中接收这些函数的参数。通常,我可以这样称呼他们:

 #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 行“序列”,我将不得不重做一百次。

如果我可以将选择映射到具有函数指针和我需要的其他信息的数据结构,那会更有意义,然后 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
      }
 }

这个带有“索引技巧”的答案让我非常接近,但由于它需要 unpack_caller 的常量,我不确定如何将其网格化到我的地图/数据结构中。

标签: c++c++14

解决方案


首先,这是在调用给定函数之前funcPointer返回一个执行 -to-arguments 杂耍的 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...>{});
}

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

最后,调用很简单:

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';
    }
}

在 Wandbox 上现场观看


推荐阅读