c++ – I don’t understand what the bind () function does in the code

Question:

Hello, I recently started studying Boost.Asio, and in one of the examples I cannot understand what the bind () function does in the code of this program. If anyone knows what this function is for, please explain. Since I recently started to dig deeper into the STL and Boost library. Thanks in advance.

 io_service service; size_t read_complete(char * buff, const error_code & err, size_t bytes) { if ( err) return 0; bool found = std::find(buff, buff + bytes, '\n') < buff + bytes; // we read one-by-one until we get to enter, no buffering return found ? 0 : 1; }

void handle_connections () {ip :: tcp :: acceptor acceptor (service, ip :: tcp :: endpoint (ip :: tcp :: v4 (), 8001)); char buff [1024]; while (true) {ip :: tcp :: socket sock (service); acceptor.accept (sock); int bytes = read (sock, buffer (buff), boost :: bind (read_complete, buff, _1, _2)); std :: string msg (buff, bytes); sock.write_some (buffer (msg)); sock.close (); }}

int main (int argc, char * argv []) {handle_connections (); }

Answer:

std:bind (I will talk about std::bind , since this function was ported to the C ++ standard from boost) is an adapter of functional objects that allows you to adapt functional objects for a given number of parameters.

To make its purpose clearer, let's say you decided to write a function to output an arbitrary integer array to the std::cout stream. Your program might look like this:

#include <iostream>

void display( const int a[], size_t n )
{    
    for ( size_t i = 0; i < n; i++ )
    {        
        std::cout << a[i] << ' ';
    }       
    std::cout << std::endl;
}

int main()
{
    const size_t N = 10;
    int a[N] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

    display( a, N );
}    

Output of the program to the console:

1 2 3 4 5 6 7 8 9 10 

But then you came up with the idea to improve the output function in such a way that it not only outputs the elements of the array as they are, but also does some transformations with them before outputting.

Therefore, you decided to add a parameter to the function that will represent some operation on the elements of the array. You declared this third parameter as a function with one parameter. Using this function, you can, for example, output to the console the doubled values ​​of array elements using a lambda expression:

#include <iostream>

void display( const int a[], size_t n, int operation( int ) )
{    
    for ( size_t i = 0; i < n; i++ )
    {        
        std::cout << operation( a[i] ) << ' ';
    }       
    std::cout << std::endl;
}

int main()
{
    const size_t N = 10;
    int a[N] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

    auto doubled = []( int x ) { return 2 * x; };
    display( a, N, doubled );
}

The console output will be

2 4 6 8 10 12 14 16 18 20 

In this case, it is possible to use the lambda expression doubled , because it can be implicitly converted into a function with one parameter, since it does not have a closure.

But let's say you decide to print not only the doubled values ​​of the array elements, but also the values ​​of the elements multiplied by a certain coefficient, which is set during program execution.

In this case, your program might look like this

#include <iostream>

void display( const int a[], size_t n, int operation( int ) )
{    
    for ( size_t i = 0; i < n; i++ )
    {        
        std::cout << operation( a[i] ) << ' ';
    }       
    std::cout << std::endl;
}

int main()
{
    const size_t N = 10;
    int a[N] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

    int  factor = 2;

    auto multiplies = [&factor]( int x ) { return factor * x; };

    display( a, N, multiplies );

    factor = 3;

    display( a, N, multiplies );
}

However, it will not compile. The problem is that if the lambda expression contains a closure,

auto multiplies = [&factor]( int x ) { return factor * x; };
                  ^^^^^^^^^

then it has no implicit conversion to a function pointer.

Therefore, you will have to write a template function that will have a template parameter of the operation and as an argument can take not only functions, but also objects of classes that have a function call operator () . Your program will look like this:

#include <iostream>

template <class Operation>
void display( const int a[], size_t n, Operation operation )
{    
    for ( size_t i = 0; i < n; i++ )
    {        
        std::cout << operation( a[i] ) << ' ';
    }       
    std::cout << std::endl;
}

int main()
{
    const size_t N = 10;
    int a[N] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

    int  factor = 2;

    auto multiplies = [&factor]( int x ) { return factor * x; };

    display( a, N, multiplies );

    factor = 3;

    display( a, N, multiplies );
}

The output to the console will accordingly be:

2 4 6 8 10 12 14 16 18 20 
3 6 9 12 15 18 21 24 27 30 

But now, let's say you decided to print the squares of the values ​​of the array elements. Again you could use a lambda expression like

auto_square = []( int x ){ return x * x; };

And your program would run successfully using that lambda expression.

But you know that the standard already has a std::multiplies function object that performs the x * y operation on the two arguments of its operator () .

How do I use it? The operator of this function object takes two arguments, whereas in our display function, the function object takes only one argument.

For this, functional adapters are used and, in particular, std::bind . It can "transform" a functional std::multiplies object from an object that takes two arguments to an object that takes one argument.

The program will look like this:

#include <iostream>
#include <functional>

template <class Operation>
void display( const int a[], size_t n, Operation operation )
{    
    for ( size_t i = 0; i < n; i++ )
    {        
        std::cout << operation( a[i] ) << ' ';
    }       
    std::cout << std::endl;
}

int main()
{
    const size_t N = 10;
    int a[N] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

    auto square = bind( std::multiplies<int>(), std::placeholders::_1, std::placeholders::_1 );

    display( a, N, square );
    std::cout << std::endl;
}    

The console output will be

1 4 9 16 25 36 49 64 81 100 

Inside the display function, the square object is called as square( a[i] ) , which in turn delegates work to the std::multiplies<int> class object, calling it as

std::multiplies<int>()( a[i], a[i] );

Of course, the std::multiplies<int> constructor is not called, since an object of this class was already created when the square object was created.

auto square = bind( std::multiplies<int>(), std::placeholders::_1, std::placeholders::_1 );

Only operator () this object was called.

In your example, the call to read requires a function object as its third argument, which only takes two arguments:

int bytes = read(sock, buffer(buff), boost::bind(read_complete,buff,_1,_2));

However, you want the read_complete function to be read_complete , which takes three arguments instead of two. In this case, the bind adapter is used, which itself receives two arguments, as required by the call to the read function, but delegates the work of the read_complete function by passing it three arguments: two of its own, designated as _1 and _2 , and which the read function passes to it inside its body, when called, and one extra, buff , which it stored internally when used as an argument to the call to read .

Scroll to Top