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

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

Author

Date

Category

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 delve deeper into the STL and Boost library. Thank you in advance.


io_service service;
size_t read_complete (char * buff, const error_code & amp; err, size_t bytes)
{
  if (err) return 0;
  bool found = std :: find (buff, buff + bytes, '\ n') & lt; 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 1, authority 100%

std: bind (I’ll talk about std :: bind since this function has been ported to the C++ standard from boost) is a functional object adapter 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 & lt; iostream & gt;
void display (const int a [], size_t n)
{
for (size_t i = 0; i & lt; n; i ++)
  {
    std :: cout & lt; & lt; a [i] & lt; & lt; '';
  }
  std :: cout & lt; & lt; 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);
}

Program output to the console:

1 2 3 4 5 6 7 8 9 10

But then you got 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 them.

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 & lt; iostream & gt;
void display (const int a [], size_t n, int operation (int))
{
  for (size_t i = 0; i & lt; n; i ++)
  {
    std :: cout & lt; & lt; operation (a [i]) & lt; & lt; '';
  }
  std :: cout & lt; & lt; 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);
}

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 to a function with one parameter, since it has no 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 some factor that is set during program execution.

In this case, your program might look like this

# include & lt; iostream & gt;
void display (const int a [], size_t n, int operation (int))
{
  for (size_t i = 0; i & lt; n; i ++)
  {
    std :: cout & lt; & lt; operation (a [i]) & lt; & lt; '';
  }
  std :: cout & lt; & lt; 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 = [& amp; 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 = [& amp; 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 class objects that have a function call operator operator () .
Your program will look like this:

# include & lt; iostream & gt;
template & lt; class Operation & gt;
void display (const int a [], size_t n, Operation operation)
{
  for (size_t i = 0; i & lt; n; i ++)
  {
    std :: cout & lt; & lt; operation (a [i]) & lt; & lt; '';
  }
std :: cout & lt; & lt; 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 = [& amp; 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 decide 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 this lambda expression.

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

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

Functional adapters are used for this and, in particular, std :: bind . It can “transform” the std :: multiplies functional object from an object that takes two arguments to an object that takes one argument.

The program will look like this:

# include & lt; iostream & gt;
#include & lt; functional & gt;
template & lt; class Operation & gt;
void display (const int a [], size_t n, Operation operation)
{
  for (size_t i = 0; i & lt; n; i ++)
  {
std :: cout & lt; & lt; operation (a [i]) & lt; & lt; '';
  }
  std :: cout & lt; & lt; 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 & lt; int & gt; (), std :: placeholders :: _ 1, std :: placeholders :: _ 1);
  display (a, N, square);
  std :: cout & lt; & lt; std :: endl;
}

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 class object std :: multiplies & lt; int & gt; ,
by calling it like

std :: multiplies & lt; int & gt; () (a [i], a [i]);

Of course, the constructor std :: multiplies & lt; int & gt; is not called because an object of this class was already created when the square

object was created

auto square = bind (std :: multiplies & lt; int & gt; (), std :: placeholders :: _ 1, std :: placeholders :: _ 1);

Only the operator () of this object was called.

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

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

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


Answer 2, authority 57%

bind this is a bit interesting function (not to be confused with socket function, also called bind !!!) … Its task is to create one function based on another, substituting part of the arguments (this is called “partial application “).

Let’s take a deeper look. The read function requires three parameters – a socket, a buffer for data and “completion function” (actually, this boost read has only eight different overloaded options). This function will be called to determine that enough data has been read. The signature of this function is also given there in the help. It has two parameters: the error code of the last reading and how many bytes were read. But what if there is no suitable function?

There’s bind for that. It takes a function as the first argument (in this case, read_complete , and then arguments. Since the original read_complete function had three arguments, and the read function only needs two, one argument is substituted explicitly (the first one), and instead of the other two, there are special stubs.

As a result, when the read function needs to call the completion function, it will call read_complete , the first argument will be substituted by bind, and the second two will be substituted with the arguments.

If you try to write the code schematically, then everything looks like this
io_service service;
size_t read_complete (char * buff, const error_code & amp; err, size_t bytes)
{
  if (err) return 0;
  bool found = std :: find (buff, buff + bytes, '\ n') & lt; buff + bytes;
  // we read one-by-one until we get to enter, no buffering
  return found? 0: 1;
}
char buff [1024];
size_t new_func (const error_code & amp; err, size_t bytes)
{
 return read_complete (buff, err, bytes);
}
void handle_connections ()
{
  ip :: tcp :: acceptor acceptor (service, ip :: tcp :: endpoint (ip :: tcp :: v4 (), 8001));
  while (true)
  {
    ip :: tcp :: socket sock (service);
    acceptor.accept (sock);
    int bytes = read (sock, buffer (buff), new_func);
    std :: string msg (buff, bytes);
    sock.write_some (buffer (msg));
    sock.close ();
  }
}

As you can see, I had to make the buffer global in order for the newly made function to have access to it. The variable does not actually become global, but this was the easiest way to emulate the behavior.

This bind function came to C++ from functional languages ​​like haskell and lisp, where similar things are found at every turn.

Programmers, Start Your Engines!

Why spend time searching for the correct question and then entering your answer when you can find it in a second? That's what CompuTicket is all about! Here you'll find thousands of questions and answers from hundreds of computer languages.

Recent questions