Function Objects

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.

Function object example

The example below demonstrates how C++11 function objects can be used with the o2scl::root_brent_gsl solver.

/* Example: ex_lambda.cpp
-------------------------------------------------------------------
Demonstrate how to use standard library and lambda function objects
with O2scl.
*/
#include <iostream>
#include <functional>
#include <o2scl/root_brent_gsl.h>
#include <o2scl/test_mgr.h>
using namespace std;
using namespace o2scl;
// A global function
double gfn(double x) {
return sin(x)-0.1;
}
class a_class {
public:
// A member function
double mfn(double x) {
return sin(x)-0.1;
}
// A member function with a parameter
double mfn_param(double x, double a) {
return sin(x)-a;
}
};
int main(void) {
cout.setf(ios::scientific);
// The O2scl solver. Note that we use the same solver for
// all the examples below.
// For the initial bracket
double a, b;
// With a global function
{
a=-0.9, b=0.9;
std::function<double(double)> f=gfn;
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,"Global function");
}
// With a member 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);
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,"Member function");
}
// With a member function which has a fixed parameter
{
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);
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,"Member function with parameter");
}
// Inline specification of the function
{
a=-0.9, b=0.9;
std::function<double(double)> f=
[](double x) -> double { double z=sin(x)-0.1; return z; };
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,"Inline 1");
}
// A bit of a shorter notation
{
a=-0.9, b=0.9;
std::function<double(double)> f=[](double x){ return sin(x)-0.1; };
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,"Inline 2");
// Show copy construction works
a=-0.9, b=0.9;
std::function<double(double)> f2=f;
grb.solve_bkt(a,b,f2);
t.test_rel(a,asin(0.1),1.0e-12,"Inline 3");
}
t.report();
return 0;
}
// End of example

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

\[ \left\{ 1+\frac{1}{p_2} \sin \left[ 50 \left( x-p_1 \right) \right] \right\} \tan^{-1} \left[ 4 \left( x-p_1 \right) \right] = 0 \]

Where $ p_1 = 0.01 $ and $ p_2 = 1.1 $. The parameter $ p_1 $ is stored as member data for the class, and the parameter $ p_2 $ is an argument to the member function.

The image below shows how the solver progresses to the solution of the example function.

ex_fptr_plot.png
Function object example plot
/* Example: ex_fptr.cpp
-------------------------------------------------------------------
This gives an example of the how member functions and external
parameters are supplied to numerical routines. In this case, a
member function with two parameters is passed to the root_brent_gsl
class, which solves the equation. One of the parameters is member
data, and the other is specified using the extra parameter argument
to the function.
*/
#include <o2scl/funct.h>
#include <o2scl/root_brent_gsl.h>
#include <o2scl/test_mgr.h>
using namespace std;
using namespace o2scl;
class my_class {
private:
double parameter;
public:
void set_parameter() { parameter=0.01; }
// A function demonstrating the different ways of implementing
// function parameters
double function_to_solve(double x, double &p) {
return atan((x-parameter)*4)*(1.0+sin((x-parameter)*50.0)/p);
}
};
// Simple code to write the function to a file
int write_file(double sol);
int main(void) {
cout.setf(ios::scientific);
// Only print something out if one of the tests fails
// The solver, specifying the type of the parameter (double)
// and the function type (funct<double>)
my_class c;
c.set_parameter();
double p=1.1;
// This is the code that allows specification of class member
// functions as functions to solve. This approach avoids the use of
// static variables and functions and multiple inheritance at the
// expense of a little overhead. We need to provide the address of
// an instantiated object and the address of the member function.
funct11 function=std::bind(std::mem_fn<double(double,double &)>
(&my_class::function_to_solve),
&c,std::placeholders::_1,p);
double x1=-1;
double x2=2;
// The value verbose=1 prints out iteration information
// and verbose=2 requires a keypress between iterations.
solver.verbose=1;
solver.solve_bkt(x1,x2,function);
// This is actually a somewhat difficult function to solve because
// of the sinusoidal behavior.
cout << "Solution: " << x1
<< " Function value: " << c.function_to_solve(x1,p) << endl;
// Write the function being solved to a file (see source code
// in examples directory for details)
write_file(x1);
// Obtain and summarize test results
t.test_abs(c.function_to_solve(x1,p),0.0,1.0e-10,"ex_fptr");
t.report();
return 0;
}
// End of example

Documentation generated with Doxygen. Provided under the GNU Free Documentation License (see License Information).