YesButWhy
YesButWhy

Reputation: 73

Why use boost::bind instead of direct function call in boost.asio operations?

I've noticed 3 main options to call handler for async operations in Boost.Asio:

class MyClass {
 public:
  void doReadFromSocket(); //implementation options provided below. 

 private:
  void handleRead(const boost::system::error_code& ec,
                  std::size_t bytesTransferred) {
    // ... handle async_read result.
   }
  
  boost::asio::streambuf m_buffer;  
  boost::asio::ip::tcp::socket m_socket;
}

Option 1 - use lambda function:

void MyClass::doReadFromSocket() {
      boost::asio::async_read(m_socket, m_bufferIn, 
                              boost::asio::transfer_at_least(1),
                              [this](const boost::system::error_code& ec,
                                     std::size_t bytesTransferred) {
                                     // ...
                                     }
     }

Option 2 - use direct member function call: UPD. The option is not correct, as pointed out in the comments.

void MyClass::doReadFromSocket() {
boost::asio::async_read(m_socket, m_bufferIn, 
                       boost::asio::transfer_at_least(1),
                       handleRead);
}

Option 3 - use boost::bind to call member function that handles the read:

void MyClass::doReadFromSocket() {
     boost::asio::async_read(m_socket, m_bufferIn,
                             boost::asio::transfer_at_least(1), 
                             boost::bind(&MyClass::handleRead, this, _1, _2));
   }
}

The main questions are:

  1. Are there any differences in those ways to handle the read operation, especially in terms of performance?
  2. Why do we need boost::bind to handle the read? Why not use a direct member function call? A lot of examples in the boost library use boost::bind to handle async operations. Also, this option seems the least convenient from my perspective

Upvotes: 1

Views: 597

Answers (1)

sehe
sehe

Reputation: 392893

Like others said, not all of your options are correct. Looking beyond that:

  • bind (std or boost) has the added effect of returning a bind-expression templated on the original functor type. In C++ this means that the original associated namespaces for ADL still apply to the bound handler. This property is important when e.g. the associated executor, allocator or cancellation slot are important (like, a strand).

    The inverse of this is a common pitfall, e.g. when using std::function<> instead of a bind expression: boost::asio::bind_executor does not execute in strand

  • boost::bind has the advantage of being available on C++03 (which Asio supports)

  • lambdas have a slight benefit of reducing the number of explicit functions defined, at the cost of increased complexity; especially the lifetime of captured variables can be harder to check.

    Also note that you might still need to manually associate an executor (asio::bind_executor) with some lambda handlers.

    The reason I feel lambdas have become more viable in generic Asio code is because in "recent" Asio versions IO objects are constructed from a specific executor, which will be the default executor for completion handlers passed to async_ initiation functions on these IO objects.

Note though that this scratches the surface of options. Contrary to what some people commented, the "handler" argument is not necessarily a handler. It's actually what's known as a completion Token. It can be many things, like use_future, use_awaitable, deferred, detached, as_tuple(another_token) etc.

Also, there are design patterns where the handler itself is a class - e.g. derived from asio::coroutine. This is often used inside the library because it provides a very lightweight way to provide "faux coroutine"-like state machines to implement asynchronous operations in a way that's compatible with C++03 and all completion tokens, without incurring unnecessary overhead.

Also note that many libraries use convenience helpers (like beast::bind_front_handler) to reduce the amount of repeated code for bind expressions.

To Summarize

It's up to you, based on specific requirements and personal preference. Asio is a highly generic framework for asynchronous operations and you have choices on how you interact with it.

Upvotes: 4

Related Questions