Lambda functions and std::mem_fn
Functions are passed to numerical routines using template-based function classes, sometimes called "functors". O2scl classes which accept functions as parameters generally default to types built upon std::function
. If the user would like to use Boost function objects instead, these may also be used, simply by specifying the Boost function type in the template parameter.
Some template aliases are defined to save typing of the function types, e.g.
- o2scl::funct : One function of one variable (used in one-dimensional solver and minimizer classes, derivative classes, integration classes, etc.)
- o2scl::multi_funct : One function of several variables (used in minimizer and integration classes)
- o2scl::mm_funct :
n
functions of n
variables (used in solver classes)
- o2scl::grad_funct : gradient function for minimizer classes
- o2scl::ode_funct :
n
derivatives as a function of n
function values and the value of the independent variable
- o2scl::ode_jac_funct : Jacobian function for ODE classes
- o2scl::ode_it_funct : Function to specify ODEs for iterative solution
- o2scl::jac_funct : Jacobian function for solver and fitting classes
- o2scl::ool_hfunct : Hessian matrix function for constrained minimization
Function object example
The example below demonstrates how C++11 function objects can be used with the o2scl::root_brent_gsl solver.
#include <iostream>
#include <functional>
#include <o2scl/root_brent_gsl.h>
#include <o2scl/test_mgr.h>
using namespace std;
double gfn(double x) {
return sin(x)-0.1;
}
class a_class {
public:
double mfn(double x) {
return sin(x)-0.1;
}
double mfn_param(double x, double a) {
return sin(x)-a;
}
};
int main(void) {
cout.setf(ios::scientific);
double a, b;
{
a=-0.9, b=0.9;
std::function<double(double)> f=gfn;
t.
test_rel(a,asin(0.1),1.0e-12,
"Global function");
}
{
a=-0.9, b=0.9;
a_class ac;
std::function<double(double)> f=
std::bind(std::mem_fn<double(double)>(&a_class::mfn),
ac,std::placeholders::_1);
t.
test_rel(a,asin(0.1),1.0e-12,
"Member function");
}
{
a=-0.9, b=0.9;
a_class ac;
std::function<double(double)> f=
std::bind(std::mem_fn<double(double,double)>(&a_class::mfn_param),
ac,std::placeholders::_1,0.1);
t.
test_rel(a,asin(0.1),1.0e-12,
"Member function with parameter");
}
{
a=-0.9, b=0.9;
std::function<double(double)> f=
[](double x) -> double { double z=sin(x)-0.1; return z; };
t.
test_rel(a,asin(0.1),1.0e-12,
"Inline 1");
}
{
a=-0.9, b=0.9;
std::function<double(double)> f=[](double x){ return sin(x)-0.1; };
t.
test_rel(a,asin(0.1),1.0e-12,
"Inline 2");
a=-0.9, b=0.9;
std::function<double(double)> f2=f;
t.
test_rel(a,asin(0.1),1.0e-12,
"Inline 3");
}
return 0;
}
General comments about function objects
The C++ standard library functors employ copy construction at various types, so one must be careful about the types involved in creating the functor. Generally, all classes should have constructors and structs should be avoided because they can cause difficulties with default copy construction.
There is a small overhead associated with the indirection: a "user
class" accesses the function class which then calls function which was specified in the constructor of the function class. In many problems, the overhead associated with the indirection is small. Some of this overhead can always be avoided by inheriting directly from the function class and thus the user class will make a direct virtual function call. To eliminate the overhead entirely, one can specify a new type for the template parameter in the user class.
Function object example
This example shows how to provide functions to O2scl classes by solving the equation
Where
and
. The parameter
is stored as member data for the class, and the parameter
is an argument to the member function.
The image below shows how the solver progresses to the solution of the example function.
Function object example plot
#include <o2scl/funct.h>
#include <o2scl/root_brent_gsl.h>
#include <o2scl/test_mgr.h>
using namespace std;
class my_class {
private:
double parameter;
public:
void set_parameter() { parameter=0.01; }
double function_to_solve(double x, double &p) {
return atan((x-parameter)*4)*(1.0+sin((x-parameter)*50.0)/p);
}
};
int write_file(double sol);
int main(void) {
cout.setf(ios::scientific);
my_class c;
c.set_parameter();
double p=1.1;
funct function=std::bind(std::mem_fn<
double(
double,
double &)>
(&my_class::function_to_solve),
&c,std::placeholders::_1,p);
cout << "Solution: " << x1
<< " Function value: " << c.function_to_solve(x1,p) << endl;
write_file(x1);
t.
test_abs(c.function_to_solve(x1,p),0.0,1.0e-10,
"ex_fptr");
return 0;
}