所以我想写一个集成功能与C ++ 11个lambda表达式中使用。 该代码看起来是这样的:
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);
};
它编译和工作正常。 我应该如何解决这个问题?
Answer 1:
使用一个void *是典型的Ç回调接口传递一些“状态”的功能。 但是,性病::功能并不需要这个,因为的std ::功能支持“状态的功能”。 所以,你可以这样做:
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;
:::
}
和参数向量的参考存储为将在标准::功能对象被封装或不喜欢这样的函子的一部分:
void Another_function()
{
double m = 2;
double b = 3;
auto func = [&](double x){return m*x+b};
auto r1 = Integrate(func,0,3);
:::
}
然而,这种解决方案将使用颇多间接引用。 GSL将调用您的拉姆达。 您的拉姆达将调用的std ::函数<> ::运算符()这反过来会inwoke某种一个用于类型擦除这又将调用实际计算虚拟功能的。
所以,如果你关心性能,你可以摆脱几个层的存在,具体的std ::功能。 下面是一个函数模板另一种方法:
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 ::功能的解决方案。
Answer 2:
貌似GSL库需要一个函数指针。 其不捕获一个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;
//...
}
Answer 3:
甲std::function<>
不能被转换为函数指针。 std::function<>
是可以潜在地保持状态,而常规功能是无状态的函数对象(样的,你可能有static
变量,但那是另一回事)。
在另一方面, 无状态的 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);
请注意,这是非法的绑定一个右值到非const引用,这样你就不能合法地通过{2,3}
作为最后一个参数来Integrate
(即使的Visual Studio可以让你),你需要创建一个名为变量。
Answer 4:
这是最好的封装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的分支预测器将能够执行还有间接将永远是相同的拉姆达。
Answer 5:
如果您需要整合与捕获lambda函数(在这种情况下,没有转化为原始指针),如果你不希望有的std ::功能(如sellibitze指出,相关的性能损失-看性病::功能VS模板 ),你可以使用下面的包装
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 ::功能,那么你可以使用这个版本的包装的
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);
那些包装的好处是,它们也可以用于整合类的成员函数。
文章来源: C function pointers with C++11 lambdas