Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The purpose of Function Binding

Tags:

c++

bind

boost

I am learning asio programming of c++ Boost library. And I have encountered many examples that use the function bind() which has function pointer as argument.

I have not been able to understand the use of bind() function. And that's why I have difficulty in understanding programs using asio of boost library.

I am not seeking for any code here. I just want to know the use of bind() function or any of its equivalent function. Thanks in advance.

like image 674
Be Wake Pandey Avatar asked Dec 05 '14 11:12

Be Wake Pandey


3 Answers

From cppreference

The function template bind generates a forwarding call wrapper for f. Calling this wrapper is equivalent to invoking f with some of its arguments bound to args.

Check the example below demonstrating bind

#include <iostream>
#include <functional>

 using namespace std;

int my_f(int a, int b)
{
    return 2 * a + b;
}

int main()
{
    using namespace std::placeholders;  // for _1, _2, _3...

     // Invert the order of arguments
     auto my_f_inv = bind(my_f, _2, _1);        // 2 args b and a
    // Fix first argument as 10
    auto my_f_1_10 = bind(my_f, 10, _1);        // 1 arg b
    // Fix second argument as 10
    auto my_f_2_10 = bind(my_f, _1, 10);        // 1 arg a
    // Fix both arguments as 10
    auto my_f_both_10 = bind(my_f, 10, 10);     // no args

    cout << my_f(5, 15) << endl; // expect 25
    cout << my_f_inv(5, 15) << endl; // expect 35
    cout << my_f_1_10(5) << endl; // expect 25
    cout << my_f_2_10(5) << endl; // expect 20
    cout << my_f_both_10() << endl; // expect 30

    return 0;
}

You can use bind to manipulate an existing function's argument order or fix some arguments. This can be particularly helpful in stl container and algorithms where you can pass an existing library function whose signature matches with your requirement.

For example if you want to transform all your doubles in your container to power 2, you can simply do something like:

std::transform(begin(dbl_vec),
               end(dbl_vec),
               begin(dbl_vec),
               std::bind(std::pow, _1, 2));

Live example here

like image 102
Mohit Jain Avatar answered Oct 02 '22 13:10

Mohit Jain


The tasks that you post to a boost::asio service have to be invokable with zero arguments so that the service can store them and invoke them once it has the spare resources (i.e. idle threads). Say you want it to call the function void purr(int kitty), in order to give this to the service in a format it can work with you need to bind the kitty argument to the purr function. This will give you an object which is invokable with () no arguments that you can provide to the service.

Of course with C++11 and lambda functions the best way to do this now is to do io_service.post([&](){ purr(3); }); and avoid using bind altogether.

like image 23
sjdowling Avatar answered Oct 02 '22 12:10

sjdowling


It allows you to associate (or "bind") your own data with a function that you want a library to call, without the library needing to know anything about your data.

Since you're looking at Boost.Asio, have a look at their tutorial for binding arguments to a handler. They have a function that they want to use as a handler, which needs pointers to their own data, the timer itself and a counter:

void print(const boost::system::error_code& /*e*/,
    boost::asio::deadline_timer* t, int* count)
{
    // ...
}

The timer's async_wait function causes a user-provided function to be called when the timer expires; but only provides the first of these arguments. The handler is of the form

void handler(
    const boost::system::error_code& error // Result of operation.
);

So we can use bind to transform our function (wanting three arguments) into one that only wants one argument, by specifying values to be passed as the other two:

t.async_wait(boost::bind(print,
    boost::asio::placeholders::error, &t, &count));

The result of bind is a function object (that is, an object of a class type that overloads the function-call operator, operator()), which in this case takes a single argument. The placeholders::error argument says that the first argument is still an argument of the new function type; the other two arguments are given the values of &t and &count when the new function is called. So if we were to call this ourselves:

auto f = boost::bind(print, boost::asio::placeholders::error, &t, &count)
f(some_error);

this would have the same effect as calling the original function with those arguments:

print(some_error, &t, &count);

Now, when the timer expires, our function is called, with arguments that we provided, without the Asio library needing to know anything about them.

like image 21
Mike Seymour Avatar answered Oct 02 '22 12:10

Mike Seymour