c++c++11boostasio

Suitable use of boost::asio::placeholders::error


The code snippet below is seen at this page.

#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
using namespace boost::asio;
using ip::tcp;
using std::cout;
using std::endl;


class con_handler : public boost::enable_shared_from_this<con_handler>
{
private:
  tcp::socket sock;
  std::string message="Hello From Server!";
  enum { max_length = 1024 };
  char data[max_length];
    
public:
    
typedef boost::shared_ptr<con_handler> pointer;
 con_handler(boost::asio::io_service& io_service)
    : sock(io_service)
  {
  }

  static pointer create(boost::asio::io_service& io_service)
  {
    return pointer(new con_handler(io_service));
  }
  
  tcp::socket& socket()
  {
    return sock;
  }

  void start()
  {
    sock.async_read_some(
        boost::asio::buffer(data, max_length),
        boost::bind(&con_handler::handle_read,
                    shared_from_this(),
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred));

    
    sock.async_write_some(
    boost::asio::buffer(message, max_length),
        boost::bind(&con_handler::handle_write, 
            shared_from_this(),
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred));
  }

  void handle_read(const boost::system::error_code& err,
                   size_t bytes_transferred)
  {
    if (!err) {
      cout << data << endl;

            

    } else {
std::cerr << "err (recv): " << err.message() << std::endl;
      sock.close();
    }
  }
  void handle_write(const boost::system::error_code& err,
               size_t bytes_transferred)
  {
    if (!err) {
 
    cout << "Server sent Hello message!"<< endl;
    
    } else {
      std::cerr << "err (recv): " << err.message() << std::endl;
      sock.close();
    }
  }

};

class Server {

private:
  tcp::acceptor acceptor_;

void start_accept()
  {
    // creates a socket
    con_handler::pointer connection =
      con_handler::create(acceptor_.get_io_service());

    // initiates an asynchronous accept operation 
    // to wait for a new connection. 
    acceptor_.async_accept(connection->socket(),
        boost::bind(&Server::handle_accept, this, connection,
          boost::asio::placeholders::error));
  }
public:
  Server(boost::asio::io_service& io_service): acceptor_(io_service, tcp::endpoint(tcp::v4(), 1234))
  { 
     start_accept();
}
    

  void handle_accept(con_handler::pointer connection,
                     const boost::system::error_code& err)
  {
    if (!err) {
      connection->start();
    }
    start_accept();
  }

};

int main(int argc, char *argv[])
{
  try 
{
    boost::asio::io_service io_service;   
    Server server(io_service);
    io_service.run();

} 
catch(std::exception& e) 
{
    std::cerr << e.what() << endl;
}
  return 0;
}

I think the code snippet should be improved. Since asio::placeholders::error is equivalent to placeholders::_1 and const boost::system::error_code& err is the second parameter for the void Server::handle_accept(con_handler::pointer connection, const boost::system::error_code& err) , the boost::asio::placeholders::error should be replaced by boost::placeholders::_2 in the the code below.

    acceptor_.async_accept(connection->socket(),
        boost::bind(&Server::handle_accept, this, connection,
          boost::asio::placeholders::error));

Solution

  • If you were thinking of the position of the parameter (_1), it is correct because the handler signature is (docs):

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

    Note that the bind expression is your handler, not handle_accept (which takes 3 arguments, including the this argument).

    If you think _1 is more expressive, go for it.

    Meanwhile,

    Simplified to this point: Live On Coliru

    #include <boost/asio.hpp>
    #include <iomanip>
    #include <iostream>
    namespace asio = boost::asio;
    using namespace std::placeholders;
    using asio::ip::tcp;
    
    struct con_handler : std::enable_shared_from_this<con_handler> {
        using pointer = std::shared_ptr<con_handler>;
        con_handler(asio::any_io_executor ex) : sock(ex) {}
    
        tcp::socket& socket() { return sock; }
    
        void start() {
            auto self = shared_from_this();
            sock.async_read_some(asio::buffer(data), bind(&con_handler::handle_read, self, _1, _2));
            sock.async_write_some(asio::buffer(message), bind(&con_handler::handle_write, self, _1, _2));
        }
    
      private:
        void handle_read(boost::system::error_code const& err, size_t bytes_transferred) {
            std::cerr << "recv: " << bytes_transferred << "(" << err.message() << ")" << std::endl;
            if (!err) {
                std::string_view msg(data.data(), bytes_transferred);
                std::cout << quoted(msg) << std::endl;
            }
            // else sock.cancel();
        }
    
        void handle_write(boost::system::error_code const& err, size_t bytes_transferred) {
            std::cerr << "sent: " << bytes_transferred << "(" << err.message() << ")" << std::endl;
            if (!err)
                std::cout << "Server sent " << quoted(message) << std::endl;
    
            // else sock.cancel();
        }
    
        tcp::socket            sock;
        std::string            message = "Hello From Server!";
        std::array<char, 1024> data;
    };
    
    struct Server {
        Server(asio::any_io_executor ex) //
            : acceptor_(ex, tcp::endpoint(tcp::v4(), 1234)) {
            start_accept();
        }
    
        void handle_accept(con_handler::pointer connection, boost::system::error_code const& err) {
            if (!err)
                connection->start();
    
            start_accept();
        }
    
      private:
        tcp::acceptor acceptor_;
    
        void start_accept() {
            con_handler::pointer connection = std::make_shared<con_handler>(acceptor_.get_executor());
    
            acceptor_.async_accept(connection->socket(), bind(&Server::handle_accept, this, connection, _1));
        }
    };
    
    int main() try {
        asio::io_context ioc;
        Server           server(ioc.get_executor());
        ioc.run();
    } catch (std::exception const& e) {
        std::cerr << e.what() << std::endl;
    }
    

    Local demo:

    Asio version: get_io_service() indicates an ancient version of boost

    Text protocols:

    v2 Live On Coliru

    #include <boost/asio.hpp>
    #include <iomanip>
    #include <iostream>
    namespace asio = boost::asio;
    using namespace std::placeholders;
    using asio::ip::tcp;
    
    struct con_handler : std::enable_shared_from_this<con_handler> {
        con_handler(tcp::socket s) : sock(std::move(s)) {}
    
        void start() {
            do_read();
            async_write(sock, asio::buffer(message), //
                        bind(&con_handler::handle_write, shared_from_this(), _1, _2));
        }
    
      private:
        void do_read() {
            async_read_until(sock, asio::dynamic_buffer(response), "\n",
                             bind(&con_handler::handle_read, shared_from_this(), _1, _2));
        }
    
        void handle_read(boost::system::error_code const& err, size_t bytes_transferred) {
            std::cerr << "recv: " << bytes_transferred << "(" << err.message() << ")" << std::endl;
            if (!err) {
                auto msg = response.substr(0, bytes_transferred - 1);
                response.erase(0, bytes_transferred);
    
                std::cout << quoted(msg) << std::endl;
                do_read();
            }
    
            // else sock.cancel();
        }
    
        void handle_write(boost::system::error_code const& err, size_t bytes_transferred) {
            std::cerr << "sent: " << bytes_transferred << "(" << err.message() << ")" << std::endl;
            if (!err)
                std::cout << "Server sent " << quoted(message) << std::endl;
    
            // else sock.cancel();
        }
    
        tcp::socket sock;
        std::string message = "Hello From Server!\n", response;
    };
    
    struct Server {
        Server(asio::any_io_executor ex) : acceptor_(ex, tcp::endpoint(tcp::v4(), 1234)) { accept_loop(); }
    
      private:
        tcp::acceptor acceptor_;
    
        void accept_loop() {
            acceptor_.async_accept([this](boost::system::error_code ec, tcp::socket s) {
                if (!ec)
                    std::make_shared<con_handler>(std::move(s))->start();
    
                accept_loop();
            });
        }
    };
    
    int main() try {
        asio::io_context ioc;
        Server           server(ioc.get_executor());
        ioc.run();
    } catch (std::exception const& e) {
        std::cerr << e.what() << std::endl;
    }
    

    With a more useful local demo: