[英]C function pointers with C++11 lambdas
所以我正在嘗試編寫一個與c ++ 11 lambdas一起使用的Integration函數。 代碼看起來像這樣:
double Integrate(std::function<double(double,void*)> func, double a,double b,std::vector<double> & params)
{
gsl_integration_workspace * w = gsl_integration_workspace_alloc (1000);
gsl_function F;
F.function =func;
F.params = (void*)¶ms;
double error,result;
gsl_integration_qag (&F, a, b, 0, 1e-7, 1000,GSL_INTEG_GAUSS61,w, &result, &error);
gsl_integration_workspace_free (w);
return result;
}
void Another_function()
{
//...
Integrate([](double a,void* param)
{
return ((vector<double> *)params)->at(0)*a+((vector<double> *)params)->at(1);
}
,0,3,{2,3});
}
試圖編譯它,編譯器說:
error: cannot convert ‘std::function<double(double, void*)>’ to ‘double (*)(double, void*)’ in assignment
關於線
F.function =func;
但如果我寫:
F.function =[](double a,void* param)
{
return ((std::vector<double> *)param)->at(0)*a+((std::vector<double> *)param)->at(1);
};
它編譯並正常工作。 我該怎么解決這個問題?
使用void *是C回調接口的典型,它將一些“狀態”傳遞給函數。 但是,std :: function不需要這個,因為std :: function支持“有狀態函數”。 所以,你可以這樣做:
double Integrate(
std::function<double(double)> func,
double a, double b)
{
typedef std::function<double(double)> fun_type;
:::
F.function = [](double x, void* p){
return (*static_cast<fun_type*>(p))(x);
};
F.params = &func;
:::
}
並存儲對參數向量的引用,作為將封裝在std :: function對象中的仿函數的一部分,或者執行以下操作:
void Another_function()
{
double m = 2;
double b = 3;
auto func = [&](double x){return m*x+b};
auto r1 = Integrate(func,0,3);
:::
}
但是,這種解決方案會使用相當多的間接。 GSL會調用你的lambda。 你的lambda將調用std :: function <> :: operator(),它反過來會喚醒某種用於類型擦除的虛函數,而這種函數又會調用實際的計算。
所以,如果你關心性能,你可以在那里擺脫幾層,特別是std :: function。 這是另一種使用函數模板的方法:
template<class Func>
double Integrate(
Func func,
double a, double b)
{
:::
F.function = [](double x, void* p)->double{
return (*static_cast<Func*>(p))(x);
};
F.params = &func;
:::
}
我想我更喜歡這個比std :: function解決方案。
看起來gsl庫需要一個函數指針。 可以將不捕獲的lambda轉換為函數指針。 任何lambda都可以轉換為std::function
。 但是std::function
無法轉換為函數指針。
你可以嘗試:
struct functor_and_params {
std::function<double(double, void*)> f;
void* params;
static double invoke(double x, void* ptr) {
functor_and_params& f_and_p = *reinterpret_cast<functor_and_params*>(ptr);
return f_and_p.f(x, f_and_p.params);
}
};
double Integrate(std::function<double(double,void*)> func,
double a,double b,std::vector<double> & params) {
functor_and_params f_and_p{ func, ¶ms };
gsl_function F;
F.function = &functor_and_params::invoke;
F.params = &f_and_p;
//...
}
std::function<>
無法轉換為函數指針。 std::function<>
是可以保持狀態的函數對象,而常規函數是無狀態的(有點,你可能有static
變量,但這是另一回事)。
另一方面, 無狀態 lambda 可以轉換為函數指針,因此您可以更改函數的簽名以直接獲取函數指針,並且將轉換lambda:
double Integrate(double(*func)(double,void*), double a, double b,
std::vector<double> & params) // !!!
std::vector<double> p{2,3};
Integrate([](double a,void* param)
{
std::vector<double> *p = static_cast<std::vector<double>*>param;
return p->at(0)*a+p->at(1);
}
,0,3,p);
請注意,將rvalue綁定到非const引用是非法的,因此您不能合法地將{2,3}
作為Integrate
的最后一個參數傳遞(即使Visual Studio允許),您將需要創建一個命名變量。
最好將void *
轉換封裝在包裝函數中:
double Integrate(std::function<double(double)> func, double a, double b)
{
gsl_integration_workspace * w = gsl_integration_workspace_alloc (1000);
gsl_function F;
F.function = [](double a, void *param) {
return (*static_cast<std::function<double(double)> *>(param))(a); };
F.params = (void*)&func;
double error,result;
gsl_integration_qag (&F, a, b, 0, 1e-7, 1000,GSL_INTEG_GAUSS61,w, &result, &error);
gsl_integration_workspace_free (w);
return result;
}
void Another_function()
{
//...
std::vector<double> params = {2, 3};
Integrate([params](double a) { return (params[0]*a+params[1]; }, 0, 3);
}
這里存在一定量的過量間接(通過std::function
),但CPU的分支預測器將能夠很好地執行,因為間接將始終是相同的lambda。
如果你需要將lambda函數與capture集成(在這種情況下沒有轉換為原始指針),並且如果你不希望與std :: function相關的性能損失(如sellibitze指出 - 請參閱std :: function vs template ),您可以使用以下包裝器
template< typename F > class gsl_function_pp : public gsl_function {
public:
gsl_function_pp(const F& func) : _func(func) {
function = &gsl_function_pp::invoke;
params=this;
}
private:
const F& _func;
static double invoke(double x, void *params) {
return static_cast<gsl_function_pp*>(params)->_func(x);
}
};
這是一個顯示如何使用它的測試代碼
double a = 1;
auto ptr = [=](double x)->double{return a*x;};
gsl_function_pp<decltype(ptr)> Fp(ptr);
gsl_function *F = static_cast<gsl_function*>(&Fp);
如果你真的想使用std :: function,那么你可以使用這個版本的包裝器
class gsl_function_pp : public gsl_function
{
public:
gsl_function_pp(std::function<double(double)> const& func) : _func(func){
function=&gsl_function_pp::invoke;
params=this;
}
private:
std::function<double(double)> _func;
static double invoke(double x, void *params) {
return static_cast<gsl_function_pp*>(params)->_func(x);
}
};
在這種情況下,測試代碼更簡單
double a = 1;
gsl_function_pp Fp([=](double x)->double{return a*x;});
gsl_function *F = static_cast<gsl_function*>(&Fp);
這些包裝器的好處是它們也可以用於集成類成員函數。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.