2014-01-10 155 views
0

考虑以下程序。有没有一种方法可以实现call()函数,而不需要在所有的if声明中?随意更改地图的类型以找到解决方案。 call()也应该抛出一个异常,如果参数数量不正确。 call()的接口可以更改,但函数的名称,参数数组的指针和参数的数量仅在运行时才可知。从具有可变数字参数的函数的指针图调用函数

#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); 
} 
+1

可变模板可能会有所帮助。 –

+0

是的,我问他们如何帮助... – Martin

+1

给他们一个尝试,找出?! –

回答

1

这里的另一个做方式。实质上,它使用OP的代码(通过if s调度),所以它不需要映射或类型擦除。但是,它需要一个尾部循环序列if - else s,而不是在地图中查找。所以查找速度较慢,但​​不需要间接指示。

// 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]...); 
    } 
}; 

与我的类型擦除方法类似,我们使用模板参数来存储函数指针。这种类型的对象将被存储在一个tuple

#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>{}); 
} 
+1

我敢打赌,你也可以在编译时的元组元素进行排序,也许建立一个编译时递归OO内的二进制搜索 – dyp

+0

这确实是另一种很好的解决方案。我会用这个。谢谢。 +1。 – Martin

1

这里有一种方法:使用类型擦除的继承。这是一个将函数指针存储为非类型模板参数的最简单示例。

另一种方法是使用std::function,它丢弃附加参数IIRC。

struct var_double_func_base 
{ 
    virtual ~var_double_func_base() {} 
    virtual double operator()(double*, int) = 0; 
}; 


// 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 <cassert> 

// a delegate-like class, storing the function to call as a non-type template 
// parameter 
template<class T, T t> 
struct var_double_fptr; 

template<class... Args, double(*fptr)(Args...)> 
struct var_double_fptr<double(*)(Args...), fptr> 
    : var_double_func_base 
{ 
    virtual double operator()(double* argv, int argc) 
    { 
     assert(argc == sizeof...(Args)); 
     return dispatch(argv, gen_seq<sizeof...(Args)>{}); 
    } 

    template<int... Is> 
    double dispatch(double* argv, seq<Is...>) 
    { 
     return fptr(argv[Is]...); 
    } 
}; 

用例:(注 - 喜欢unique_ptr,但这并不均匀初始化地图的工作)。

#include <memory> 
#include <map> 
#include <string> 
#include <cmath> 

typedef double(*PF1)(double); 
typedef double(*PF2)(double, double); 
typedef double(*PF3)(double, double, double); 

std::map<std::string, std::shared_ptr<var_double_func_base>> m = { 
    {"sin", std::make_shared<var_double_fptr<PF1, std::sin>>()}, 
    {"pow", std::make_shared<var_double_fptr<PF2, std::pow>>()} 
    // other 
}; 

double call(std::string name, double* argv, int argc) 
{ 
    auto i = m.find(name); 
    assert(i != m.end()); 
    return (*(i->second))(argv, argc); 
} 

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); 
} 
+0

对不起,迟到的答案。这工作也TEACHED我如何产生通过可变参数模板整数序列,这是我在那里停留在点。 +1。我将分析你的第二个答案,并选择最好的答案。谢谢。 – Martin

+0

@马丁注意,我们会得到'的std :: integer_sequence','的std ::用C make_integer_sequence'等++ 1Y – dyp