Document number:

N3702

Date:

2013-06-28

Project:

Programming Language C++, Library Working Group

Reply-to:

Mikhail Semenov<mikhailsemenov1957@gmail.com>

 

Introducing an optional parameter for mem_fn, which allows to bind an object to its member function

1.     Introduction

When a member function is used as a parameter to another function (the latter is usually called a functional) it is often necessary to provide the corresponding class object as a parameter as well. But functionals, like for example an integral, are often written to accept one global function. In cases when a member function and an object are needed, it is necessary to bind them together,  so that the result can be supplied to the corresponding functional as one parameter. Let us consider the following functional integrate (it can be part of  a library): 

double integrate(std::function<double(double, double, double, double, double)> f,

                     double x1, double x2, double y1, double y2, double z1, double z2,  double a1, double a2, double b1, double b2) ;

We need to call integrate with  the parameterized function fa, which is defined as a member of the class A:

class A

{

     double p;

 

public:

     A(double p1):p(p1) {}

     void set_param(double p1) {  p = p1; }

     double fa(double x, double y, double z, double a, double b);

};

 

The member-variable  p is a "hidden" parameter of the function fa. We create an object:

A a(5.0);

If we want to call integrate with a.fa  we have three options:

(1)   double r =  integrate(std::bind(&A::fa, &a, _1, _2, _3, _4, _5), x1, x2, y1,y2, z1, z2,  

                                       a1, a2, b1, b2);

 

(2)    double r = integrate

              ([&a](double x, double y, double z, double a, double b) { return a.fa(x, y, z, a, b);},     

                 x1, x2, y1, y2, z1, z2, a1, a2, b1, b2); 

 

      (3)   define a global function (a wrapper), which we will use as a  parameter to the function integrate:

                       double f(double x, double y, double z, double a, double b) { return a.f(x,y,z,a,b); }

               

         double r = integrate(f, x1, x2, y1, y2, z1, z2, a1, a2, b1, b2);

       

In all these examples, if the function fa had more parameters we would have to write even a longer statement.

 

It would be much more convenient to be able to write something like this:

double r = integrate(some_binder(&A::fa, &a), x1, x2, y1, y2, z1, z2, a1, a2, b1, b2); 

which looks more simple and clearer.

2.     Proposal

The proposal is to allow mem_fn to accept a second, optional parameter. The full definition is as follows (the first option is the same as in N3690, section 20.10.10):

template<class R, class T> unspecified mem_fn(R T::* pm);

Returns: A simple call wrapper (20.10.1) fn such that the expression fn(t, a2, ..., aN) is equivalent to INVOKE (pm, t, a2, ..., aN) (20.10.2). fn shall have a nested type result_type that is a synonym for the return type of pm when pm is a pointer to member function.

The simple call wrapper shall define two nested types named argument_type and result_type as synonyms for cv T* and Ret, respectively, when pm is a pointer to member function with cv-qualifier cv and taking no arguments, where Ret  is pm’s return type.

The simple call wrapper shall define three nested types named first_argument_type, second_argument_type, and result_type as synonyms for cv  T*, T1, and Ret, respectively, when pm is a pointer to member function with cv-qualifier cv and taking one argument of type T1, where Ret is pm’s return type.

Throws: Nothing.

template<class R, class T> unspecified mem_fn(R T::*, cv T* obj);

Returns: A simple call wrapper (20.10.1) fn such that the expression fn(a1, ..., aN) is equivalent to INVOKE (pm, obj, a1, ..., aN) (20.10.2). fn shall have a nested type result_type that is a synonym for the return type of pm when pm is a pointer to member function.

The simple call wrapper shall define one nested type named result_type as a synonym for Ret, when pm is a pointer to member function with cv-qualifier cv and taking no arguments, where Ret  is pm’s return type.

The simple call wrapper shall define two nested types named argument_type and result_type as synonyms for T1 and Ret, respectively, when pm is a pointer to member function with cv-qualifier cv and taking  one argument of type T1, where Ret is pm’s return type.

The simple call wrapper shall define three nested types named first_argument_type, second_argument_type, and result_type as synonyms for T1, T2, and Ret, respectively, when pm is a pointer to member function with cv-qualifier cv and taking two arguments of types T1 and  T2, where Ret is pm’s return type.

Throws: Nothing.

The second option means that the object and the member function will be bound together.  Using this feature we can now write the following code (the class and the function used here were explained in the introduction):

double x = 1.0;

double sum = 0.0;

for (int i = 0; i < 10; i++)

{

   a.set_param(x);

   sum += integrate(mem_fn(&A::fa, &a), x1, x2, y1, y2, z1, z2, a1, a2, b1, b2); // summing up ten values

   x += 0.1;

}

Examples

Example 1.

Here is a sample program, which shows the application of  mem_fn to simple member functions, and also its use with a shared pointer  as the second parameter:

#include <iostream>

#include <functional>

#include <memory>

 

class A2

{

    int i;

public:   

    A2(int k):i(k) {}

 

    auto get()const ->int { return i;}   

    auto set(int v)->void { i = v;}

 

    auto inc(int g)->int& { i+=g; return i;}

    auto incp(int& g)->int& { g+=i; return g;}

   

    auto f8 (int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) const -> int

    {

        return i+a1+a2+a3+a4+a5+a6+a7+a8;

    }       

};

 

 

int main()

{

    A2 a(11); 

   

    auto a_get = mem_fn(&A2::get,&a);   

    std::cout << a_get() << std::endl;

    auto a_get1 = mem_fn(&A2::get);

       

    std::cout << a_get1(&a) << std::endl;

 

    auto set1 = mem_fn(&A2::set, &a);

   

    std::shared_ptr<A2> sh_a(new A2(22));

    auto inc = mem_fn(&A2::inc, sh_a);

    auto incp = mem_fn(&A2::incp, sh_a);

    auto af8  = mem_fn(&A2::f8,sh_a);

       

                                    

    set1(15);

    std::cout << "a.get():" << a.get() << std::endl;       

 

    int x = 5;

    int k = inc(x);

    int& k2 = incp(x);

   

    k2 *= 7;

    std::cout << "a.get():" << a.get() << std::endl;

    std::cout << "k: " << k << std::endl;

    std::cout << "x: " << x << std::endl;

std::cout << "af8(1,2,3,4,5,6,7,8): "

           << af8(1,2,3,4,5,6,7,8) << std::endl;   

}

This program will print:

11

11

a.get():15

a.get():15

k: 27

x: 224

af8(1,2,3,4,5,6,7,8): 63

 

Example 2.

This program  shows how to apply mem_fn to  overloaded member functions:

#include <iostream>

#include <functional>

#include <vector>

 

class B

{

    double x;

    std::vector<double> y;

public:

    B(double x1):x(x1),y(3)

    {

        y[0] = 1.5;

        y[1] = 2.7;

        y[2] = 4.9;

    }

          

   

    std::vector<double> add_vector(std::vector<double>&& z)

    {      

        std::cout << "move" << std::endl;   

        for (int i = 0; i < 3; i++)

        {

            z[i] += y[i];

        }

        return std::move(z);

    }

   

 

 

 

    std::vector<double> add_vector(const std::vector<double>& z) const

    {     

        std::cout << "copy" << std::endl;   

        std::vector<double> c(z);  

        for (int i = 0; i < 3; i++)

        {

            c[i] += y[i];

        }

        return c;

    }

   

    void add_vector(std::vector<double>& z)

    {     

        std::cout << "out" << std::endl;           

        for (int i = 0; i < 3; i++)

        {

            z[i] += y[i];

        }       

    }

};

 

int main()

{   

    B b(3.5);

   

    std::vector<double> v(3);

    v[0] = 10.0;

    v[1] = 100.0;

    v[2] = 1000.0;  

   

auto b_add_vector_copy =

   std::mem_fn

     ((std::vector<double>(B::*)(const std::vector<double>&) const)

       &B::add_vector, &b);

 

    std::vector<double> v1 = b_add_vector_copy(v);

    std::cout << "v.size(): " << v.size() << std::endl;

    for (int i = 0; i < 3; i++)

    {

        std::cout << "v1: " << v1[i] << std::endl;

    }

      

auto b_add_vector_move =

     std::mem_fn

       ((std::vector<double>(B::*)(std::vector<double>&&))

        &B::add_vector,&b);

    std::vector<double> v2 = b_add_vector_move(std::move(v));

    std::cout << "v.size(): " << v.size() << std::endl; // v size is now 0

    for (int i = 0; i < 3; i++)

    {

        std::cout << "v2: " << v2[i] << std::endl;

    }

 

    std::vector<double> v3(3);

    v3[0] = 10.0;

    v3[1] = 100.0;

    v3[2] = 1000.0;    

   

   

auto b_add_vector_ref

     std::mem_fn

         ((void(B::*)(std::vector<double>&))&B::add_vector,&b);

    b_add_vector_ref(v3);

    for (int i = 0; i < 3; i++)

    {

        std::cout << "v3: " << v3[i] << std::endl;

    }

}

 

This program will print:

copy

v.size(): 3

v1: 11.5

v1: 102.7

v1: 1004.9

move

v.size(): 0

v2: 11.5

v2: 102.7

v2: 1004.9

out

v3: 11.5

v3: 102.7

v3: 1004.9

 

3.     Implementation

namespace std

{

 

#define _two_param_delegate2(_SIG, _NAME)\

template<class _OBJECT1, class _T, class _R, class _Arg1, class _Arg2,\

    class ... _Arg>\

class _delegate2 ## _NAME\

{\

    _OBJECT1 _obj;\

    _R (_T::*f1)(_Arg1, _Arg2, _Arg ... ) _SIG;\

public:\

    typedef _Arg1 first_argument_type;\

    typedef _Arg2 second_argument_type;\

    typedef _R return_type;\

\

_delegate2 ## _NAME(_R (_T::*_fx)(_Arg1, _Arg2, _Arg ... )\

           _SIG, _OBJECT1 _obj1):_obj(_obj1),f1(_fx)\

    {\

    } \

\

    _R operator()(_Arg1 _x1, _Arg2 _x2, _Arg ...  _x) _SIG\

    {\

        return ((*_obj).*f1)(std::forward<_Arg1>(_x1),\

        std::forward<_Arg2>(_x2), std::forward<_Arg>(_x) ...);\

    }\

};

 

 

 

 

_two_param_delegate2(,_simple)

_two_param_delegate2(const, _const)

_two_param_delegate2(const volatile, _const_volatile)

_two_param_delegate2(volatile, _volatile)

 

 

#define _two_param_delegate1(_SIG, _NAME)\

template<class _OBJECT1, class _T, class _R, class _Arg1>\

class _delegate1 ## _NAME\

{\

    _OBJECT1 _obj;\

    _R (_T::*f1)(_Arg1) _SIG;\

public:\

    typedef _Arg1 argument_type;\

    typedef _R return_type;\

\

_delegate1 ## _NAME(_R (_T::*_fx)(_Arg1)\

       _SIG, _OBJECT1 _obj1):_obj(_obj1),f1(_fx)\

    {\

    } \

\

    _R operator()(_Arg1 _x1) _SIG\

    {\

        return ((*_obj).*f1)(std::forward<_Arg1>(_x1));\

    }\

};

 

_two_param_delegate1(,_simple)

_two_param_delegate1(const, _const)

_two_param_delegate1(const volatile, _const_volatile)

_two_param_delegate1(volatile, _volatile)

 

#define _two_param_delegate0(_SIG, _NAME)\

template<class _OBJECT1, class _T, class _R>\

class _delegate0 ## _NAME\

{\

    _OBJECT1 _obj;\

    _R (_T::*f1)() _SIG;\

public:\

    typedef _R return_type;\

\

    _delegate0 ## _NAME(_R (_T::*_fx)() _SIG, _OBJECT1 _obj1):_obj(_obj1),f1(_fx)\

    {\

    } \

\

    _R operator()() _SIG\

    {\

        return ((*_obj).*f1)();\

    }\

};

 

_two_param_delegate0(,_simple)

_two_param_delegate0(const, _const)

_two_param_delegate0(const volatile, _const_volatile)

_two_param_delegate0(volatile, _volatile)

 

 

 

 

#define _one_param_delegate1(_SIG, _NAME)\

template<class _T, class _R, class _Arg1, class ... _Arg>\

class _one_p_delegate1 ## _NAME\

{\

    _R (_T::*f1)(_Arg1, _Arg ... ) _SIG;\

public:\

    typedef _SIG _T* first_argument_type;\

    typedef _Arg1 second_argument_type;\

    typedef _R return_type;\

\

    _one_p_delegate1 ## _NAME(_R (_T::*_fx)(_Arg1, _Arg ... ) _SIG):f1(_fx)\

    {\

    } \

\

    template<class _OBJECT>\

    _R operator()(_OBJECT _obj,_Arg1 _x1, _Arg ...  _x) _SIG\

    {\

        return ((*_obj).*f1)(std::forward<_Arg1>(_x1), std::forward<_Arg>(_x) ...);\

    }\

};

 

_one_param_delegate1(,_simple)

_one_param_delegate1(const, _const)

_one_param_delegate1(const volatile, _const_volatile)

_one_param_delegate1(volatile, _volatile)

 

#define _one_param_delegate0(_SIG, _NAME)\

template<class _T, class _R>\

class _one_p_delegate0 ## _NAME\

{\

    _R (_T::*f1)() _SIG;\

public:\

    typedef _SIG _T* argument_type;\

    typedef _R return_type;\

\

    _one_p_delegate0 ## _NAME(_R (_T::*_fx)() _SIG):f1(_fx)\

    {\

    } \

\

    template<class _OBJECT>\

    _R operator()(_OBJECT _obj) _SIG\

    {\

        return ((*_obj).*f1)();\

    }\

};

 

_one_param_delegate0(,_simple)

_one_param_delegate0(const, _const)

_one_param_delegate0(const volatile, _const_volatile)

_one_param_delegate0(volatile, _volatile)

 

#define _mem_fn_macro2(_SIG,_NAME)\

template<class _OBJECT, class _T, class _R, class _Arg1, class _Arg2, class ... _Arg>\

_delegate2 ## _NAME<_OBJECT,_T,_R,_Arg1, _Arg2, _Arg ... >\

 mem_fn(_R (_T::*_f)(_Arg1, _Arg2, _Arg ... ) _SIG, _OBJECT _obj)\

{\

    return _delegate2 ## _NAME <_OBJECT,_T,_R, _Arg1, _Arg2, _Arg ... >(_f, _obj);\

}

 

_mem_fn_macro2(,_simple)

_mem_fn_macro2(const,_const)

_mem_fn_macro2(const volatile, _const_volatile)

_mem_fn_macro2(volatile, _volatile)

 

#define _mem_fn_macro1(_SIG,_NAME)\

template<class _OBJECT, class _T, class _R, class _Arg1>\

_delegate1 ## _NAME<_OBJECT,_T,_R,_Arg1>\

 mem_fn(_R (_T::*_f)(_Arg1) _SIG, _OBJECT _obj)\

{\

    return _delegate1 ## _NAME <_OBJECT,_T,_R, _Arg1>(_f, _obj);\

}

 

_mem_fn_macro1(,_simple)

_mem_fn_macro1(const,_const)

_mem_fn_macro1(const volatile, _const_volatile)

_mem_fn_macro1(volatile, _volatile)

 

 

 

#define _mem_fn_macro0(_SIG,_NAME)\

template<class _OBJECT, class _T, class _R>\

_delegate0 ## _NAME<_OBJECT,_T,_R> mem_fn(_R (_T::*_f)() _SIG, _OBJECT _obj)\

{\

    return _delegate0 ## _NAME <_OBJECT,_T,_R>(_f, _obj);\

}

 

_mem_fn_macro0(,_simple)

_mem_fn_macro0(const,_const)

_mem_fn_macro0(const volatile, _const_volatile)

_mem_fn_macro0(volatile, _volatile)

 

#define _one_p_mem_fn_macro1(_SIG,_NAME)\

template<class _T, class _R, class _Arg1, class ... _Arg>\

_one_p_delegate1 ## _NAME<_T,_R,_Arg1, _Arg ... >\

     mem_fn(_R (_T::*_f)(_Arg1, _Arg ... ) _SIG)\

{\

    return _one_p_delegate1 ## _NAME <_T,_R,_Arg1, _Arg ... >(_f);\

}

_one_p_mem_fn_macro1(,_simple)

_one_p_mem_fn_macro1(const,_const)

_one_p_mem_fn_macro1(const volatile, _const_volatile)

_one_p_mem_fn_macro1(volatile, _volatile)

 

#define _one_p_mem_fn_macro0(_SIG,_NAME)\

template<class _T, class _R>\

_one_p_delegate0 ## _NAME<_T,_R> mem_fn(_R (_T::*_f)() _SIG)\

{\

    return _one_p_delegate0 ## _NAME <_T,_R>(_f);\

}

 

_one_p_mem_fn_macro0(,_simple)

_one_p_mem_fn_macro0(const,_const)

_one_p_mem_fn_macro0(const volatile, _const_volatile)

_one_p_mem_fn_macro0(volatile, _volatile)

 

}