c++ - 从指向具有可变数量参数的函数的指针映射调用函数

标签 c++ c++11

考虑以下程序。有没有一种方法可以在主体中没有所有 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 的代码(通过 ifs 调度),因此它不需要映射或类型删除。但是,它需要 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/

相关文章:

c++ - 为什么我使用这些意大利面条模板得到 "error: type name is not allowed"?

c++ - 使用集合方法从未排序的数组中删除重复项

c++ - 错误 C3861 : popen, pclose:在 C++ 中找不到标识符

c++ - 为什么 basic_stringbuf 和 basic_filebuf 移动构造函数具有实现定义的行为?

c++ - 转发通用可调用对象的返回值

c++ - 匹配模板别名作为模板模板参数

c++ - 使用Boost.Spirit解析具有混合数据类型的OBJ文件?

c++ - QML/D 文本编辑器和基本注意事项

c++ - 与类定义相同类型的静态 constexpr 成员(其他详细信息)

c++ - 在C++中将字符串转换为整数数组