考虑以下程序。有没有一种方法可以在主体中没有所有 if
语句的情况下实现 call()
函数?随意更改 map 类型以找到解决方案。 call()
还应该在参数数量错误的情况下抛出异常。 call()
的接口(interface)可以更改,但函数名称、指向参数数组的指针和参数数量仅在运行时已知。
#include <iostream>
#include <string>
#include <map>
#include <cmath>
#include <boost/any.hpp>
using namespace std;
typedef double(*PF1)(double);
typedef double(*PF2)(double, double);
typedef double(*PF3)(double, double, double);
map<string, boost::any> m = {
{"sin", static_cast<PF1> (std::sin)},
{"pow", static_cast<PF2> (std::pow)}
// other
};
double call(string name, double* args, int nargs) {
if (name == "sin" && nargs == 1)
return boost::any_cast<PF1>(m[name])(args[0]);
else if (name == "pow" && nargs == 2)
return boost::any_cast<PF2>(m[name])(args[0], args[1]);
// etc...
}
int main() {
double n[] = {1, 2, 3, 4, 5, 6};
int narg1 = 1, narg2 = 2; // known at runtime
double r = call("sin", n, narg1);
r = call("pow", n, narg2);
}
最佳答案
这是另一种方法。本质上,它使用 OP 的代码(通过 if
s 调度),因此它不需要映射或类型删除。但是,它需要 if
-else
的尾部循环序列,而不是在映射中进行查找。所以查找速度较慢,但不需要间接寻址。
// a helper type to generate and deduce a sequence of integers
template<int...> struct seq {};
template<int N, int... Is> struct gen_seq : gen_seq<N-1, N-1, Is...> {};
template<int... Is> struct gen_seq<0, Is...> : seq<Is...> {};
#include <string>
#include <cassert>
template<class T, T t>
struct c_fptr;
template<class... Args, double(*fptr)(Args...)>
struct c_fptr<double(*)(Args...), fptr>
{
std::string name;
double operator()(double* argv, int argc) const
{
assert(argc == sizeof...(Args));
return dispatch(argv, gen_seq<sizeof...(Args)>{});
}
template<int... Is>
double dispatch(double* argv, seq<Is...>) const
{
return fptr(argv[Is]...);
}
};
与我的类型删除方法类似,我们使用模板参数来存储函数指针。这种类型的对象将存储在一个元组
#include <map>
#include <cmath>
typedef double(*PF1)(double);
typedef double(*PF2)(double, double);
typedef double(*PF3)(double, double, double);
#include <tuple>
auto x = std::make_tuple(
c_fptr<PF1, std::sin>{"sin"}
, c_fptr<PF2, std::pow>{"pow"}
);
此元组现在用于递归函数调用,其中每个步骤检查元组元素之一的名称是否与传递的字符串匹配:
// end recursion
double call_recurse(std::string const& name, double* argv, int argc)
{
throw std::invalid_argument("name not found");
}
template<class T, class... TT>
double call_recurse(std::string const& name, double* argv, int argc,
T const& l, TT const&... rest)
{
if(name == l.name)
{
return l(argv, argc);
}else
{
return call_recurse(name, argv, argc, rest...);
}
}
template<int... Is>
double call_dispatch(std::string const& name, double* argv, int argc, seq<Is...>)
{
return call_recurse(name, argv, argc, std::get<Is>(x)...);
}
double call(std::string const& name, double* argv, int argc)
{
return call_dispatch(name, argv, argc,
gen_seq<std::tuple_size<decltype(x)>::value>{});
}
关于c++ - 从指向具有可变数量参数的函数的指针映射调用函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21049681/