gsl_function alternative for c++


gsl_function alternative for c++



I am switching from C to C++, and I would like to optimally use the additional features that become available and avoid things considered 'C-style' like void * pointers. Specifically, I am trying to make a gsl_function-like interface (NOT a wrapper to use gsl in C++).


void *


gsl_function



In C, I wrote several routines for root-finding, integration ... that use a gsl_function-like interface to pass mathematical functions to these routines. This interface looks like this:


gsl_function


struct Function_struct
{
double (* p_func) (double x, void * p_params);
void * p_params;
};
typedef struct Function_struct Function;

#define FN_EVAL(p_F,x) (*((p_F)->p_func))(x,(p_F)->p_params)



and can be used in the following way:


struct FuncParams_struct { double a; double b; double c; };

double my_sqrt(double x, void * p) {
struct FuncParams_struct * p_params = (struct FuncParams_struct *) p;
double a = p_params->a;
double b = p_params->b;
double c = p_params->c;

return a/sqrt(x+b)+c;
}

Function My_Sqrt;
My_Sqrt.p_func = &my_sqrt;
struct FuncParams_struct my_params = { 1.0, 1.0, 0.0 };
My_Sqrt.p_params = &my_params;

// Call the function at a certain x
double result_at_3 = FN_EVAL(&My_Sqrt, 3);

// Pass My_Sqrt to an integration routine
// (which does not care about the parameters a,b,c
// and which can take any other Function to integrate)
// It uses FN_EVAL to evaluate MySqrt at a certain x.
double integral = integrate(&My_Sqrt);

// Easily change some of the parameters
My_Sqrt.p_params->a=3.0;



As you can see, this allows me to create an instance of the Function structure, which contains a pointer to some function parameters (which are typically contained within another structure) and a pointer to a 'normal' C function. The great thing about this is that the routines that use Function only need to know that it is a function that takes a double and returns a double (they use the FN_EVAL macro). They do not have to care about the type and number of parameters. On the other hand, I can easily change the values stored in the parameters from outside of the routines.


Function


Function


FN_EVAL



I would now like to have the features highlighted above in a C++ Function. I searched a lot for what would be the best way to get this, but I could not find it (partly because I am a bit overwhelmed by the possibilities of C++ as compared to C). Thus far, I was thinking to make a class Function. This class could than store the actual function definition and it could be made callable by defining the operator(), such that again routines do not have to care about the parameters. The operator() could thus be used instead of the FN_EVAL macro.


class Function


class


operator()


operator()


FN_EVAL



The things that I could not yet decide/find are:


void *


class Function



Things to consider:


<functional>



I would like to have the possibility to extend the Function class to higher dimensions, i.e. f(x,y,z). In C, I had for example


Function class


struct FunctionThree_struct
{
double (* p_func) (double x, double y, double z, void * p_params);
void * p_params;
};
typedef struct FunctionThree_struct FunctionThree;
#define FN_THREE_EVAL(p_F,x,y,z) (*((p_F)->p_func))(x,y,z,(p_F)->p_params)



I do care about efficient function calls. Especially for higher dimensional integrals, the functions will be called millions of times.





You should take a look at the std::function class template and the std::bind template function for binding known parameters and placeholders. :)
– Thomas Russell
Jun 29 at 9:42



std::function


std::bind





And of course learn about lambdas (which can often replace std::bind).
– Some programmer dude
Jun 29 at 9:45


std::bind




2 Answers
2



Your example in idiomatic C++11 using std::function/std::bind might look something like the following:


std::function


std::bind


#include <functional>

double sqrt_fn(double x, double a, double b, double c);
double integrate(double x_0, double x_1, std::function<double(double)>);

void foo() {
// Get parameters a, b, c
double a = ..., b = ..., c = ...;
double x_0 = ..., x_1 = ...;
double integral = integrate(x_0, x_1, std::bind(&sqrt_fn, std::placeholders::_1, a, b, c));
}



This is quite general, but std::function internally uses type-erasure and so there is some overhead there. If there are performance requirements here, then you can consider using a lambda function and making integrate a template function that accepts a generic Callable. This would look something like:


std::function


integrate


Callable


double sqrt_fn(double x, double a, double b, double c);
template <typename Func>
double integrate(double x_0, double x_1, Func f);

void foo() {
// Get parameters a, b, c
double a = ..., b = ..., c = ...;
double x_0 = ..., x_1 = ...;
double integral = integrate(x_0, x_1, [a, b, c](double x) { return sqrt_fn(x, a, b, c) });
}



And is more transparent to the optimizer and involves no type-erasure.





This last solution looks nice! If I understand well, I can also put a, b and c in a structure and pass a reference to this structure? The function declaration would then be double sqrt_fn(double x, struct& mystructure). But how should I write the lambda then? [ ,&mystructure](double x){ return sqrt_fn(x, mystructure)} or [mystructure](double x){return sqrt_fn(x,mystructure)}? Moreover, in the definition of integrate, can I evaluate the function just as f(3)? As you might see, I have never used a lambda expression before...
– JorenV
Jun 29 at 12:11



double sqrt_fn(double x, struct& mystructure)


[ ,&mystructure](double x){ return sqrt_fn(x, mystructure)}


[mystructure](double x){return sqrt_fn(x,mystructure)}


f(3)



In C++ you can do all that using templates and std::function. For example:


std::function


// your header file
#include <functional>
namespace myNumerics {
// version taking std::function argument, implemented in source
double integrate(double a, double b, std::function<double(double)> const&func,
double err=1.e-9);

// template version taking any callable object, including function pointers
template<typename Func>
inline double integrate(double a, double b, Func const&func, double err=1.e-9)
{
return integrate(a,b,std::function<double(double)>(func),err);
}
}

// your source code, implements the numerics
double myNumerics::integrate(double a, double b,
std::function<double(double)> const&func,
double err)
{
some clever code here (but do not re-invent the wheel again);
}

// application code, uses the template version
void application(double a, double b, double c)
{
// ...
auto f = myNumerics::integrate(0., 1., [=](double x) { return a/sqrt(x+b)+c; });
// ...
}



In particular, don't bother with std::bind (which is a C++ dinosaur from the dark ages before lambda expressions). std::function is a very powerful method to encapsulate essentially anything that can be called, allowing you to implement the nitty-gritty details of your code in a well hidden source code, i.e. no need to expose the details in a template version (which necessarily must reside in a header file).


std::bind


std::function



Note also that, unlike the situation with C, you can overload the function (here integrate()) to take different types of arguments. Here, I used a specific version taking std::function and a general templated version taking anything. It will compile as long as std::function can be initialized from whatever func object is passed (in case of an error, the compiler message may be rather cryptic, though).


integrate()


std::function


std::function


func



This generality of std::function comes at the price of one indirection, creating a small overhead. Note, however, that this overhead is also present in your C implementation (via the Function.p_func pointer). Typically, that overhead is not critical, but in case it is, you can expose the implementation directly as a template in the header file. This avoids the overhead, but requires compilation of every instance (of the integrate() routine) you use.


std::function


Function.p_func


template


integrate()





@ThomasRussell Yes, of course. fixed. thanks.
– Walter
Jun 29 at 9:55





Thank you for the clear explanation. It seems like this implementation is very similar to the second solution of @ThomasRussell, except that you suggest to encapsulate everything in an std::function, which avoids a compilation of every instance? The application code with the lambda expression will use the template version, I think?
– JorenV
Jun 29 at 12:32


std::function





@JorenV yes, that's right (I have added a corresponding remark to the answer).
– Walter
Jun 29 at 13:11






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Comments

Popular posts from this blog

paramiko-expect timeout is happening after executing the command

Export result set on Dbeaver to CSV

Opening a url is failing in Swift