2014-01-10 113 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