Function Objects

O2scl

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;
  }
  
};

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");
  }

  // ─────────────────────────────────────────────────────────────────
  // 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

\[\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.

alt text
/* 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<long double(long double)> o2scl::funct_ld

One-dimensional function typedef in src/base/funct.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.