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.
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.
You should take a look at the
std::function
class template and thestd::bind
template function for binding known parameters and placeholders. :)– Thomas Russell
Jun 29 at 9:42