C function pointers with c ++ 11 lambdas

So, I'm trying to write an integration function that will be used with lambdas C ++ 11. The code looks something like this:

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*)&params; 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}); } 

Trying to compile this, the compiler says:

 error: cannot convert 'std::function<double(double, void*)>' to 'double (*)(double, void*)' in assignment 

about line

 F.function =func; 

But if I write:

 F.function =[](double a,void* param) { return ((std::vector<double> *)param)->at(0)*a+((std::vector<double> *)param)->at(1); }; 

It compiles and works fine. How do i solve this?

+6
c ++ c ++ 11 gsl
source share
5 answers

Using void * is typical of C-reverse C interfaces to pass some "state" to a function. However, std :: function does not need this because std :: function supports "stateful functions". So you can do something like this:

 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; ::: } 

and save the link to the parameter vector as part of a functor that will be encapsulated in a std :: function object or to do something like this:

 void Another_function() { double m = 2; double b = 3; auto func = [&](double x){return m*x+b}; auto r1 = Integrate(func,0,3); ::: } 

However, this solution will use quite a bit of guidance. GSL will reference your lambda. Your lambda will call the function std :: function <>: :(), which in turn will call some kind of virtual function, which is used to erase the type, which, in turn, will call the actual calculation.

So, if you care about performance, you can get rid of a couple of layers, in particular, std :: function. Here is another approach with a function template:

 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; ::: } 

I think I would prefer this by the solution of std :: function.

+4
source share

It seems like the gsl library needs a function pointer. A lambda that does not capture can be converted to a function pointer. Any lambda can be converted to std::function . But std::function cannot be converted to a function pointer.

You can try:

 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, &params }; gsl_function F; F.function = &functor_and_params::invoke; F.params = &f_and_p; //... } 
+3
source share

A std::function<> cannot be converted to a function pointer. std::function<> are function objects that can potentially hold state, while regular functions are static (for example, you could have static variables, but that’s completely different).

On the other hand, stateless lambdas can be converted to a function pointer, so you could change the signature of your function to use the function pointer directly, and the lambdas will be converted:

 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); 

Please note that it is illegal to bind the value of r to a non-constant reference, so you cannot legally pass {2,3} as the last argument to Integrate (even if Visual Studio allows you), you will need to create a named variable.

+3
source share

It is best to encapsulate the void * conversion into your shell:

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

There is a certain excessive indirection (via std::function ), but the processor branch predictor will be able to work well, since the direction will always correspond to the same lambda.

+3
source share

If you need to integrate a lambda function with a capture (in this case there is no conversion to a raw pointer), and if you do not want to have performance penalties associated with the std :: function (as indicated by sellibitze - see std :: function vs template ) you can use the following shell

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

Here is a test code that shows how to use it

  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); 

If you really want to use std :: function, you can use this version of the shell

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

The test code is even simpler in this case.

 double a = 1; gsl_function_pp Fp([=](double x)->double{return a*x;}); gsl_function *F = static_cast<gsl_function*>(&Fp); 

The good thing about these shells is that they can also be used to integrate member functions of a class.

+3
source share

All Articles