Function Objects¶
Function object contents¶
Lambda functions and std::mem_fn
¶
Functions are passed to numerical routines using template-based
function classes, sometimes called “functors”. O₂scl
classes which accept functions as parameters generally default to
types built upon std::function
. Alternative
function objects can be used by changing the associated template
parameter.
Some template aliases are defined to save typing of the function types, e.g.
funct : One function of one variable (used in one-dimensional solver and minimizer classes, derivative classes, integration classes, etc.)
funct_ld : One function of one variable using long double precision
multi_funct : One function of several variables (used in minimizer and integration classes)
mm_funct: \(n\) functions of \(n\) variables (used in solver classes)
grad_funct: gradient function for minimizer classes
ode_funct: \(n\) derivatives as a function of \(n\) function values and the value of the independent variable
ode_jac_funct: Jacobian function for ODE classes
ode_it_funct: Function to specify ODEs for iterative solution
jac_funct : Jacobian function for solver and fitting classes
fit_funct: Fit function
ool_hfunct: Hessian matrix function for constrained minimization
First function object example¶
The example below demonstrates how C++ function objects and lambda expressions can be used with the root_brent_gsl solver. This lengthy example demonstrates several different function objects in different kinds of functions. The same methods apply to other O₂scl function objects.
/* Example: ex_lambda.cpp
-------------------------------------------------------------------
Demonstrate how to use standard library and lambda function objects
with O2scl. See "License Information" section of the documentation
for license information.
*/
#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;
}
// A global function with a value parameter
double gfn_param(double x, double p) {
return sin(x)-p;
}
// A global function with a reference parameter
double gfn_rparam(double x, double &p) {
return sin(x)-p;
}
// A global function with a const reference parameter
double gfn_crparam(double x, const double &p) {
return sin(x)-p;
}
class a_class {
public:
// A member function
double mfn(double x) {
return sin(x)-0.1;
}
// A const member function
double cmfn(double x) const {
return sin(x)-0.1;
}
// A member function with a value parameter
double mfn_param(double x, double p) {
return sin(x)-p;
}
// A member function with a reference parameter
double mfn_rparam(double x, double &p) {
return sin(x)-p;
}
// A const member function with a reference parameter
double cmfn_rparam(double x, double &p) const {
return sin(x)-p;
}
// A const member function with a const reference parameter
double cmfn_crparam(double x, const double &p) const {
return sin(x)-p;
}
// A member function which operates on a const reference
double mfncr(const double &x) {
return sin(x)-0.1;
}
// A const member function which operates on a const reference
double cmfncr(const double &x) const {
return sin(x)-0.1;
}
// A member function with a value parameter which operates on a
// const reference
double mfncr_param(const double &x, double p) {
return sin(x)-p;
}
// A member function with a reference parameter which operates on a
// const reference
double mfncr_rparam(const double &x, double &p) {
return sin(x)-p;
}
// A const member function with a reference parameter which operates
// on a const reference
double cmfncr_rparam(const double &x, double &p) const {
return sin(x)-p;
}
// A const member function with a const reference parameter which
// operates on a const reference
double cmfncr_crparam(const double &x, const double &p) const {
return sin(x)-p;
}
};
template<class type1_t> class b_class {
public:
/// An example of class data with a template type
type1_t y;
// A const member function with a const reference parameter which
// operates on a const reference to a generic floating point type
template<class fp_t>
fp_t cmfncr_crparam(const fp_t &x, const fp_t &p) const {
return sin(x)-p+1.0-static_cast<fp_t>(y);
}
};
int main(void) {
test_mgr t;
t.set_output_level(2);
cout.setf(ios::scientific);
// The O2scl solver. Note that we use the same solver for
// all the examples below.
root_brent_gsl<> grb;
// For the initial bracket
double a, b;
// The parameter
double p=0.1;
// 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 using std::function");
}
// O2scl defines 'funct' as std::function<double(double)>, so this
// shorter notation also works.
{
a=-0.9, b=0.9;
funct f=gfn;
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,"Global function");
}
// A global function with a parameter
{
a=-0.9, b=0.9;
funct f=std::bind(gfn_param,std::placeholders::_1,p);
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,"Global function with parameter");
}
// A global function with a reference parameter, note that
// std::ref() is used to indicate a reference parameter
{
a=-0.9, b=0.9;
funct f=std::bind(gfn_rparam,std::placeholders::_1,std::ref(p));
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,
"Global function with reference parameter");
}
// A global function with a const reference parameter,
// note that std::cref() is used for the const reference
{
a=-0.9, b=0.9;
funct f=std::bind(gfn_crparam,std::placeholders::_1,std::cref(p));
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,
"Global function with const reference parameter");
}
// With a member function
{
a=-0.9, b=0.9;
a_class ac;
funct 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 const member function, note the extra const in the
// template parameter for std::mem_fn
{
a=-0.9, b=0.9;
a_class ac;
funct f=std::bind(std::mem_fn<double(double) const>(&a_class::cmfn),
ac,std::placeholders::_1);
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,"Const member function");
}
// With a member function which has a value parameter.
{
a=-0.9, b=0.9;
a_class ac;
funct 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");
}
// With a member function which has a reference parameter.
// Note the use of std::ref() for the reference parameter.
{
a=-0.9, b=0.9;
a_class ac;
funct f=std::bind(std::mem_fn<double(double,double &)>
(&a_class::mfn_rparam),
ac,std::placeholders::_1,std::ref(p));
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,"Member function with reference parameter");
}
// With a const member function which has a reference parameter Note
// the use of const in the template parameter for std::mem_fn.
{
a=-0.9, b=0.9;
a_class ac;
funct f=std::bind(std::mem_fn<double(double,double &) const>
(&a_class::cmfn_rparam),
ac,std::placeholders::_1,std::ref(p));
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,
"Const member function with reference parameter");
}
// With a const member function which has a const reference
// parameter. Note the use of const in the template parameter
// for std::mem_fn and the use of std::cref() for the reference
// parameter.
{
a=-0.9, b=0.9;
a_class ac;
funct f=std::bind(std::mem_fn<double(double,const double &) const>
(&a_class::cmfn_crparam),
ac,std::placeholders::_1,std::cref(p));
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,
"Const member function with const reference parameter");
}
// ─────────────────────────────────────────────────────────────────
// This section shows that one can use functions which take const
// references as inputs rather than values. This distinction is
// important when you are creating function pointers to functions
// which operate on more complicated classes.
// With a member function
{
a=-0.9, b=0.9;
a_class ac;
funct f=std::bind(std::mem_fn<double(const double &)>(&a_class::mfncr),
ac,std::placeholders::_1);
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,"Member function");
}
// With a const member function, note the extra const in the
// template parameter for std::mem_fn
{
a=-0.9, b=0.9;
a_class ac;
funct f=std::bind(std::mem_fn<double(const double &) const>
(&a_class::cmfncr),
ac,std::placeholders::_1);
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,"Const member function");
}
// With a member function which has a value parameter.
{
a=-0.9, b=0.9;
a_class ac;
funct f=std::bind(std::mem_fn<double(const double &,double)>
(&a_class::mfncr_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");
}
// With a member function which has a reference parameter.
// Note the use of std::ref() for the reference parameter.
{
a=-0.9, b=0.9;
a_class ac;
funct f=std::bind(std::mem_fn<double(const double &,double &)>
(&a_class::mfncr_rparam),
ac,std::placeholders::_1,std::ref(p));
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,
"Member function with reference parameter");
}
// With a const member function which has a reference parameter Note
// the use of const in the template parameter for std::mem_fn.
{
a=-0.9, b=0.9;
a_class ac;
funct f=std::bind(std::mem_fn<double(const double &,double &) const>
(&a_class::cmfncr_rparam),
ac,std::placeholders::_1,std::ref(p));
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,
"Const member function with reference parameter");
}
// With a const member function which has a const reference
// parameter. Note the use of const in the template parameter
// for std::mem_fn and the use of std::cref() for the reference
// parameter.
{
a=-0.9, b=0.9;
a_class ac;
funct f=std::bind(std::mem_fn<double(const double &,
const double &) const>
(&a_class::cmfncr_crparam),
ac,std::placeholders::_1,std::cref(p));
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,
"Const member function with const reference parameter");
}
// The same as above, but now, with a class template which has
// a member function template. In this case, we choose both
// the class and member function types at compile time.
{
a=-0.9, b=0.9;
b_class<int> bc;
bc.y=1;
funct f=std::bind(std::mem_fn<double(const double &,
const double &) const>
(&b_class<int>::cmfncr_crparam<double>),
bc,std::placeholders::_1,std::cref(p));
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,
"Const member func. with const ref. param. (w/templates)");
}
// ─────────────────────────────────────────────────────────────────
// A few examples which show how to use this with a slightly
// different function type, one which accepts a const reference
typedef std::function<double(const double &)> funct2;
root_brent_gsl<funct2> grb2;
{
a=-0.9, b=0.9;
a_class ac;
funct2 f=std::bind(std::mem_fn<double(const double &)>(&a_class::mfncr),
ac,std::placeholders::_1);
grb2.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,"Member function");
}
{
a=-0.9, b=0.9;
a_class ac;
funct2 f=std::bind(std::mem_fn<double(double,
const double &) const>
(&a_class::cmfn_crparam),
ac,std::placeholders::_1,std::cref(p));
grb2.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,
"Const member function with const reference parameter");
}
{
a=-0.9, b=0.9;
a_class ac;
funct2 f=std::bind(std::mem_fn<double(const double &,
const double &) const>
(&a_class::cmfncr_crparam),
ac,std::placeholders::_1,std::cref(p));
grb2.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,
"Const member function with const reference parameter");
}
// ─────────────────────────────────────────────────────────────────
// This section shows how you can use lambda expressions similar to
// the function pointers above
// Lambda expression with inline specification
{
a=-0.9, b=0.9;
funct 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,"Lambda inline");
}
// Lambda expression for global function with parameter.
{
a=-0.9, b=0.9;
funct f=[p](double x) -> double { return gfn_param(x,p); };
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,
"Lambda for global function with parameter");
}
// Lambda expression for global function with reference parameter.
// We require the 'mutable' keyword for the parameter 'p' because
// default captures are const, and p is a non-const reference.
{
a=-0.9, b=0.9;
funct f=[p](double x) mutable -> double { return gfn_rparam(x,p); };
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,
"Lambda for global function with reference parameter");
}
// Lambda expression for global function with const reference parameter
{
a=-0.9, b=0.9;
funct f=[p](double x)-> double { return gfn_crparam(x,p); };
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,
"Lambda for global function with const reference parameter");
}
// Lambda expression for member function. We require the 'mutable'
// keyword for the class instance 'ac' because default captures are
// const, and mfn is not a const member function.
{
a=-0.9, b=0.9;
a_class ac;
funct f=[ac](double x) mutable -> double { return ac.mfn(x); };
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,
"Lambda for member function");
}
// Lambda expression for const member function
{
a=-0.9, b=0.9;
a_class ac;
funct f=[ac](double x) -> double { return ac.cmfn(x); };
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,"Lambda for const member function");
}
// Lambda expression for member function with value parameter. We
// require the 'mutable' keyword for the class instance 'ac' because
// default captures are const, and mfn is not a const member
// function.
{
a=-0.9, b=0.9;
a_class ac;
funct f=[ac,p](double x) mutable -> double { return ac.mfn_param(x,p); };
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,
"Lambda for member function with parameter");
}
// Lambda expression for member function with reference parameter.
// We require the 'mutable' keyword for both 'ac' and 'p'.
{
a=-0.9, b=0.9;
a_class ac;
funct f=[ac,p](double x) mutable -> double { return ac.mfn_rparam(x,p); };
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,
"Lambda for member function with reference parameter");
}
// Lambda expression for const member function with reference
// parameter. We require the 'mutable' keyword for the parameter 'p'
// because default captures are const, and p is a non-const
// reference.
{
a=-0.9, b=0.9;
a_class ac;
funct f=[ac,p](double x) mutable -> double { return ac.cmfn_rparam(x,p); };
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,
"Lambda for const member function with reference parameter");
}
// Lambda expression for const member function with const reference
// parameter. This is the case when 'mutable' is not required, because
// both the reference and the member function are const.
{
a=-0.9, b=0.9;
a_class ac;
funct f=[ac,p](double x)-> double { return ac.cmfn_crparam(x,p); };
grb.solve_bkt(a,b,f);
t.test_rel(a,asin(0.1),1.0e-12,
((std::string)"Lambda for const member function with ")+
"const reference parameter");
}
t.report();
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. Classes without constructors and structs should be used with care 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.
Second function object example¶
This example shows how to provide functions to O₂scl classes by solving the equation
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.
/* 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. See "License Information" section of the documentation
for license information.
*/
#include <fstream>
#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 member_function(double x, double &p) {
return atan((x-parameter)*4)*(1.0+sin((x-parameter)*50.0)/p);
}
};
// This header contains the code for write_file()
#include "ex_fptr.h"
int main(void) {
cout.setf(ios::scientific);
test_mgr t;
// Only print something out if one of the tests fails
t.set_output_level(1);
// The solver, specifying the type of the parameter (double)
// and the function type (funct<double>)
root_brent_gsl<> solver;
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.
funct function=std::bind(std::mem_fn<double(double,double &)>
(&my_class::member_function),
&c,std::placeholders::_1,std::ref(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.member_function(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.member_function(x1,p),0.0,1.0e-10,"ex_fptr");
t.report();
return 0;
}
Function typedefs¶
-
typedef std::function<double(double)> o2scl::funct¶
One-dimensional function typedef in src/base/funct.h.
-
typedef std::function<int(double, double&)> o2scl::funct_ret¶
One-dimensional function typedef in src/base/funct.h.
-
typedef std::function<long double(long double)> o2scl::funct_ld¶
One-dimensional long double function in src/base/funct_multip.h.
-
typedef std::function<double(size_t, const boost::numeric::ublas::vector<double>&)> o2scl::multi_funct¶
Multi-dimensional function typedef in src/base/multi_funct.h.
-
typedef std::function<int(size_t, const boost::numeric::ublas::vector<double>&, boost::numeric::ublas::vector<double>&)> o2scl::mm_funct¶
Array of multi-dimensional functions typedef in src/base/mm_funct.h.
-
typedef std::function<int(size_t, boost::numeric::ublas::vector<double>&, boost::numeric::ublas::vector<double>&)> o2scl::grad_funct¶
Array of multi-dimensional functions typedef in src/min/mmin.h.
-
typedef std::function<int(double, size_t, const boost::numeric::ublas::vector<double>&, boost::numeric::ublas::vector<double>&)> o2scl::ode_funct¶
Ordinary differential equation function in src/ode/ode_funct.h.
-
typedef std::function<int(double, size_t, const boost::numeric::ublas::vector<double>&, boost::numeric::ublas::matrix<double>&, boost::numeric::ublas::vector<double>&)> o2scl::ode_jac_funct¶
Functor for ODE Jacobians in src/ode/ode_jac_funct.h.
-
typedef std::function<double(size_t, double, boost::numeric::ublas::matrix_row<boost::numeric::ublas::matrix<double>>&)> o2scl::ode_it_funct¶
Function for iterative solving of ODEs.
-
typedef std::function<int(size_t, boost::numeric::ublas::vector<double>&, size_t, boost::numeric::ublas::vector<double>&, boost::numeric::ublas::matrix<double>&)> o2scl::jac_funct¶
Jacobian function (not necessarily square) in src/root/jacobian.h.
-
typedef std::function<double(size_t, const boost::numeric::ublas::vector<double>&, double)> o2scl::fit_funct¶
Array of multi-dimensional functions typedef (C++11 version) in src/fit/fit_base.h.
-
typedef std::function<int(size_t, const boost::numeric::ublas::vector<double>&, const boost::numeric::ublas::vector<double>&, boost::numeric::ublas::vector<double>&)> o2scl::ool_hfunct¶
Hessian product function for o2scl::mmin_constr.