Search code examples
c++lambdaboost

Translating a c++ lambda function to a class method function in boost


I am a new self taught C++ programmer trying to learn a bit more.

I am attempting to modify the boost ssl example located here:

https://www.boost.org/doc/libs/1_82_0/doc/html/boost_asio/example/cpp11/ssl/server.cpp

I am using g++ compiler and c++20

there is a lambda function in the do_accept() method in the server class:

acceptor_.async_accept(
     [this](const boost::system::error_code& error, tcp::socket socket)
     {
       if (!error)
       {
         std::make_shared<session>(
             boost::asio::ssl::stream<tcp::socket>(
               std::move(socket), context_))->start();
       }

       do_accept();
     });

I would like to change the lambda function to a class method.

My method is:

void server::acceptHandler(const boost::system::error_code& error, tcp::socket socket) {
...
}

When I try to substitute to this: acceptor_.async_accept(boost::bind(&server::acceptHandler, this, boost::asio::placehoders::error, tcp::socket));

I get "error: expected primary-expression before ')' token" on the ')' after tcp::socket

I also tried: acceptor_.async_accept(boost::bind(&server::acceptHandler, this, boost::asio::placehoders::error, boost::placeholders:_2))

I get: "error: use of deleted function 'boost::asio::basic_stream_socket<Protocol,Executor>::basic_stream_socket<Protocol,Executor&>) [with Protocol = boost::asio::ip::tcp; Executor = boost::asio::any_io_executor'

What is the proper method of changing a lambda function to a method function.

Here is the full code for the accept handler:

void acceptHandler(const boost::system::error_code& error, tcp::socket socket) {
if (!error)
  {
    std::make_shared<session>(
        boost::asio::ssl::stream<tcp::socket>(
          std::move(socket), context_))->start();
  }

 do_accept();
}

I get the "error: use of deleted function..." on the std::move(socket)


Solution

  • I imagined some of the surrounding code:

    Live On Coliru

    #include <boost/asio.hpp>
    #include <boost/asio/ssl.hpp>
    #include <iostream>
    namespace asio = boost::asio;
    namespace ssl  = asio::ssl;
    using asio::ip::tcp;
    
    struct session : std::enable_shared_from_this<session> {
        session(boost::asio::ssl::stream<tcp::socket> s) : stream_(std::move(s)) {}
        void start() {
            std::cout << "Accepted connection from " << stream_.lowest_layer().remote_endpoint() << "\n";
            stream_.async_handshake(ssl::stream_base::server,
                                    [self = shared_from_this()](boost::system::error_code ec) {
                                        if (!ec) {
                                            self->do_read();
                                        } else {
                                            std::cerr << "Handshake failed: " << ec.message() << "\n";
                                        }
                                    });
        }
    
      private:
        void do_read() {
            stream_.async_read_some(
                asio::buffer(data_),
                [self = shared_from_this()](boost::system::error_code ec, size_t bytes_transferred) {
                    if (!ec) {
                        std::cout << "Received " << bytes_transferred << " bytes\n";
                        self->do_read();
                    } else {
                        std::cerr << "Read failed: " << ec.message() << "\n";
                    }
                });
        }
        ssl::stream<tcp::socket> stream_;
        std::array<char, 1024>   data_;
    };
    
    struct Server {
        Server(asio::any_io_executor ex, uint16_t port) : acc_(ex, {{}, port}) {
            // TODO load certificate and private key
            do_accept();
        }
    
      private:
        void do_accept() {
            acc_.async_accept([this](boost::system::error_code const& error, tcp::socket socket) {
                if (!error) {
                    std::make_shared<session>(ssl::stream<tcp::socket>(std::move(socket), context_))->start();
                }
    
                do_accept();
            });
        }
        tcp::acceptor acc_;
        ssl::context  context_{ssl::context::tlsv12};
    };
    
    int main() {
        asio::thread_pool ctx;
    
        Server srv(ctx.get_executor(), 8989);
    
        ctx.join();
    }
    

    Because I left the certificate as an exercise for the reader, this is about what it can do:

    enter image description here

    Changing the lambda

    Now, I don't know why you want it, but you can:

    void do_accept() {
        acc_.async_accept(std::bind( //
            &Server::on_accept, this, std::placeholders::_1, std::placeholders::_2));
    }
    
    void on_accept(boost::system::error_code const& error, tcp::socket socket) {
        if (!error) {
            std::make_shared<session>(ssl::stream<tcp::socket>(std::move(socket), context_))->start();
        }
    
        do_accept();
    }
    

    Note that tcp::socket is a type, not a placeholder.