Sunday, February 23, 2025

function

 
Overview
The function wrapper classes provide alternative to function pointers. It's basically a  general-purpose polymorphic function wrapper. 

Details
The function wrapper classes provide alternative to function pointers. It's basically a  general-purpose polymorphic function wrapper. Instances of function classes can store, copy, and invoke function pointers, lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members.

The syntax is as shown below:
//generic type. 
//defined for specialization
template< typename T>
class function; /* undefined */

//R return type
//Args arguments
template< typename Ret, class... Args >
class function<R(Args...)>;

A decay copy of the wrapped callable object is stored internally by the object, which becomes the function's target member. The specific type of this target callable object is not needed in order to instantiate the function wrapper class; only its call signature.

function class objects can also be in a state with no target callable object. In this case they are known as empty functions, and calling them throws a bad_function_call exception.

constructors
Allocator based constructors are also available. 
NameDescription
function()
function (nullptr_t fn)
Default constructor with no target
function (const function & fn)Copy constructor. Copies target from fn to this.
function (Fn fn)The object stores a decayed copy of fn as its target.
Example
int square_fn(int x) {return x*x;}

// a function object class:
struct square_functor 
{
    int operator()(int x) {return x*x;}
};

// a class with data members:
struct square_struct 
{
    int x;
    int square() {return x*x;}
};


    //function
    function<int(int)> fn = square_fn;
    //returns 16
    fn(4);
    
    //pointer to function
    function<int(int)> fnptr = &square_fn;
    //returns 16
    fnptr(4);
    
    //function object
    function<int(int)> fnobj = square_functor();
    //returns 16
    fnobj(4);
    
    // lambda expression
    function<int(int)> fnlamb = [](int x){return x*x;};  
    //returns 16
    fnlamb(4);
    
    //predefined function object
    function<int(int,int)> fnmul = multiplies<int>();
    //returns 16
    fnlamb(4);
 
    // pointer to data member
    function<int&(square_struct&)> value = &square_struct::x;  
    // pointer to member function
    function<int(square_struct&)> sqr = &square_struct::square;  

    square_struct t {};

    //t.x=4
    value(t)=4;

    //returns 16
    sqr(t);

methods
NameDescription
operator bool()Returns whether the object is callable.i.e., if it has a callable object as target.

Example
    function<int(int,int)> fnmul;
    bool b =  static_cast<bool>(fnmul);
    
    fnmul = multiplies<int>();
    b =  static_cast<bool>(fnmul);
Ret operator()
(Args... args)
Calls the target callable object, forwarding args as arguments.

The effect depends on the type of the callable object targeted by the function object:

If the target is a function pointer or a function object, it is called forwarding the arguments to the call.
If the target is a pointer to a non-static member function, it is called using the first argument as the object on which the member is called (this may either be an object, a reference, or a pointer to it), and the remaining arguments are forwarded as arguments for the member function.
If it is a pointer to a non-static data member, it should be called with a single argument, and the function returns a reference to that member of its argument (the argument may either be an object, a reference, or a pointer to it).
If the object has no target (it is an empty function), it throws a bad_function_call exception.

Example
struct mul_struct 
{
    int x;
    int mul(int y) {return x*y;}
};

    square_struct a{10};
    function<int(square_struct&)> fnx = &square_struct::x;
    function<int(square_struct&,int)> fnmul = &square_struct::mul;
    //prints 10
    cout << fnx(a);
    //prints 100
    cout << fnmul(a,10);
TargetType* target()
Returns a pointer to the callable object stored in the function object.

Because function is a polymorphic wrapper class, it is unaware of the static type of its target callable object, and thus the template parameter TargetType must be explicitly specified.

TargetType shall match the target type, so that typeid(TargetType)==target_type(). Otherwise, the function always returns a null pointer.

Example
int add(int a,  int b){return a+b;}
int sub(int a,  int b){return a-b;}

function<int(int,int)> f = &add;
function<int(int,int)> f2 = plus<int>();

//returns 120
(*f.target<int(*)(int,int)>())(100,20) << endl;

//returns 120
(*f2.target<plus<int>>())(100,20);

*f.target<int(*)(int,int)>() = &sub;
//returns 80
f(100,20);
const type_info& target_type()Returns the type_info object that identifies the type of the target.

Example
function<int(int,int)> f = add;
function<int(int,int)> f2 = &add;
function<int(int,int)> f3 = plus<int>();

//int (__cdecl*)(int,int)
cout << f.target_type().name() << endl;
//int (__cdecl*)(int,int)
cout << f2.target_type().name() << endl;
//struct std::plus<int>
cout << f3.target_type().name() << endl;
The example 7 following lists the usage.

placeholder namespace
This namespace defines an unspecified number of objects: _1_2_3,..._N, where _N is implementation specific upper limit. 
syntax
namespace placeholders 
{  
	extern /* unspecified */ _1;  
	extern /* unspecified */ _2;  
	//....
	extern /* unspecified */ _N;  
}

These are used to specify placeholders in calls to function bind. When the function object returned by bind is called, an argument with placeholder::_1 is replaced by the first argument in the call, placeholder::_2 is replaced by the second argument in the call, and so on.

Example
void printnum(int n) {cout << n << endl;}
 
    auto pnum = bind(printnum,placeholders::_1);

    //prints 10
    pnum(10);

is_placeholder class
is_placeholder trait class can be used to check if the argument is a placeholder type or not.

syntax
template< class T >
struct is_placeholder;
If T is the type of a standard placeholder (_1, _2, _3, ...), then this template is derived from std::integral_constant<int, 1>, std::integral_constant<int, 2>, std::integral_constant<int, 3>, respectively.
If T is not a standard placeholder type, this template is derived from std::integral_constant<int, 0>.
bind uses is_placeholder to detect placeholders for unbound arguments.

Example
    //prints 1
    cout << is_placeholder<decltype(placeholders::_1)>::value << endl;
    //prints 2
    cout << is_placeholder<decltype(placeholders::_2)>::value << endl;
    //prints 0
    cout << is_placeholder<int>::value << endl;

mem_fn
Function template mem_fn generates wrapper objects for pointers to members, which can store, copy, and invoke a pointer to member. Both references and pointers (including smart pointers) to an object can be used when invoking a mem_fn.

syntax
template <class Ret, class T>  
/* unspecified */ mem_fn (Ret T::* pm);

Example
struct Msg
{
    char buf[50];
    void print(){cout << buf;};
} m;
 
    auto  pbuf = mem_fn(&Msg::buf);
    char str[] = "hello, world!";
    auto len = char_traits<char>::length(str);
    //sets m.buf to "hello, world"
    char_traits<char>::copy(pbuf(m),str,len);    

    auto  pprint = mem_fn(&Msg::print);
    //prints "hello, world"
pprint(m);
The example 10 demonstrates its usage.

bind
Returns a function object based on fn, but with its arguments bound to args.

syntax
template <class Fn, class... Args>  
/* unspecified */ bind (Fn&& fn, Args&&... args);

template <class Ret, class Fn, class... Args>  
/* unspecified */ bind (Fn&& fn, Args&&... args);
  • fn is a function object, pointer to function or pointer to member.
  • args is a list of arguments. Each argument may either be bound to a value or be a placeholder or a reference wrapper or another bind expression. If bound to a value, calling the returned function object will always use that value as argument otherwise the value passed in the call will be forwarded at runtime. 
  • ret A function object that, when called, calls fn with its arguments bound to args.
Example
struct Msg
{
    char buf[50];
    void print(){cout << buf;};
} m;


    char str[] = "hello, world!";
    auto len = char_traits<char>::length(str);
    //sets m.buf to "hello, world"
    auto pbuf = bind(&Msg::buf,placeholders::_1);
    char_traits<char>::copy(pbuf(m),str,len);

    //calls m.print();
    //prints hello, world
    auto pprint = bind(&Msg::print,placeholders::_1);
    pprint(m);

The example 11 following lists the usage:
 struct MyPair 
        {
            double a,b;
            double multiply() {return a*b;}
        } ten_two {10,2};
    
bind (divides<int>{},10,2)()						 = 5
bind (divides<int>{},placeholders::_1,2)(10)				 = 5
bind (divides<int>{},placeholders::_2,placeholders::_1)(10,2)		 = 0
bind<int> (divides<int>{},placeholders::_1,placeholders::_2)(10,3)	 = 3
bind (&MyPair::multiply,placeholders::_1)(ten_two)			 = 20
bind (&MyPair::a,ten_two)()						 = 10
The example 12 following lists the usage:
    void f(int n1, int n2, int n3, const int& n4, int n5);
        int g(int n1);
 
        struct Foo
        {
            void print_sum(int n1, int n2);
        int data = 10;
        } foo;
    
1) argument reordering and pass-by-reference: 
 bind(f, placeholders::_2, 42, placeholders::_1, cref(n), n)(1, 2, 1001) = 2 42 1 10 7

2) achieving the same effect using a lambda: 
[&ncref = n, n](auto a, auto b, auto /*unused*/)(1, 2, 1001) = 2 42 1 10 7

3) nested bind subexpressions share the placeholders: 
bind(f, placeholders::_3, bind(g, placeholders::_3), placeholders::_3, 4, 5)(10, 11, 12) = 12 12 12 4 5

4) bind a RNG with a distribution: 
bind(uniform_int_distribution<>(0, 10), default_random_engine{})() = 1
5) bind to a pointer to member function: 
bind(&Foo::print_sum, &foo, 95, placeholders::_1) = 100

6) bind to a mem_fn that is a pointer to member function: 
bind(mem_fn(&Foo::print_sum), &foo, 95, placeholders::_1) = 100

7) bind to a pointer to data member: 
bind(&Foo::data, placeholders::_1)(foo) = 10

8) bind to a mem_fn that is a pointer to data member: 
bind(mem_fn(&Foo::data), placeholders::_1)(foo) = 10

9) use smart pointers to call members of the referenced objects: 
bind(mem_fn(&Foo::data), placeholders::_1)(make_shared<Foo>(foo)) = 10
bind(mem_fn(&Foo::data), placeholders::_1)(make_unique<Foo>(foo)) = 10



No comments:

Post a Comment