Search code examples
c++c++11boost-asiorvalue-reference

rvalue reference to boost asio completion handler


In boost::asio, is it possible to use move semantics and rvalue reference emulation in C++11 to create and implement completion handlers?

My attempt is below, but I don't understand the reason for the errors I am getting:

Class Definition:

struct SocketTest : std::enable_shared_from_this<SocketTest> {


  SocketTest(boost::asio::ip::udp::socket socket):socket_(std::move(socket)){}

  template<typename Handler>
  void async_receive(dummy dummy,Handler&& handler){
    auto self(shared_from_this());

    socket_.async_receive(boost::asio::buffer(buf_), 
       std::bind([this,self](
          boost::system::error_code ec, 
          std::size_t bytes_transferred, 
          Handler&& moved_handler
       ){
           moved_handler(ec,bytes_transferred);
        }, std::move(handler)));

  }

  std::array<char,max_length> buf_;
  boost::asio::ip::udp::socket socket_;
};

Example of calling the class:

SocketTest socket_test(std::move(s));

socket_test.async_receive(dummy(), []( boost::system::error_code ec, std::size_t bytes_transferred){
    // many cool things accomplished!
});

It seems the version below which copies the handler works correctly, but I am interested in learning how to avoid that copy:

struct SocketTest : std::enable_shared_from_this<SocketTest> {


  SocketTest(boost::asio::ip::udp::socket socket):socket_(std::move(socket)){}

  template<typename Handler>
  void async_receive(dummy dummy,Handler handler){
    auto self(shared_from_this());

    socket_.async_receive(boost::asio::buffer(buf_), [this,self,handler](boost::system::error_code ec, std::size_t bytes_transferred){
       handler(ec,bytes_transferred);

    });

  }

  std::array<char,max_length> buf_;
  boost::asio::ip::udp::socket socket_;
};

The full source and errors can be found here


Solution

  • You can't bind to a callable object taking rvalue reference because when bind saves its argument, it's not an rvalue anymore. In your case you can just make it accept Handler by value (and this is the final place where the handler is stored). You can see this question for further details.

    Also, you forgot to provide placeholders to bind:

    socket_.async_receive(boost::asio::buffer(buf_), 
       std::bind([this,self](
          boost::system::error_code ec, 
          std::size_t bytes_transferred, 
          Handler moved_handler
       // ^^^^^^^ passing by value 
       ){
           moved_handler(ec,bytes_transferred);
        }, std::placeholders::_1, std::placeholders::_2, std::move(handler)));
        // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ placeholders added