Search code examples
c++boostboost-asio

Boost 1.73.0 asio has error when i use get_executor


my ubuntu version is 20.04LTS and Boost version is 1.73 it's my code :

#include "server.h"
#include <iostream>

Server::Server(boost::asio::io_context& io_context, unsigned short port)
    : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)) {
    start_accept();
}

void Server::start_accept() {
    tcp::socket socket(acceptor_.get_executor().context());
    acceptor_.async_accept(socket,
        [this](boost::system::error_code ec, tcp::socket socket) {
            handle_accept(std::move(socket), ec);
        }
    );
}

void Server::handle_accept(tcp::socket socket, const boost::system::error_code& error) {
    if (!error) {
        std::cout << "New connection accepted." << std::endl;
    } else {
        std::cerr << "Error on accept: " << error.message() << std::endl;
    }
    start_accept();
}

in

tcp::socket socket(acceptor_.get_executor().context());

it always has error like error: ‘class boost::asio::execution_context’ has no member named ‘get_executor’

it's complete output

g++ -I include -std=c++17 -Wall -c src/server.cpp -o obj/server.o
In file included from /usr/include/boost/asio/basic_socket.hpp:21,
                 from /usr/include/boost/asio/basic_datagram_socket.hpp:20,
                 from /usr/include/boost/asio.hpp:24,
                 from include/server.h:4,
                 from src/server.cpp:1:
/usr/include/boost/asio/detail/io_object_impl.hpp: In instantiation of ‘boost::asio::detail::io_object_impl<IoObjectService, Executor>::io_object_impl(ExecutionContext&, typename std::enable_if<std::is_convertible<ExecutionContext&, boost::asio::execution_context&>::value>::type*) [with ExecutionContext = boost::asio::execution_context; IoObjectService = boost::asio::detail::reactive_socket_service<boost::asio::ip::tcp>; Executor = boost::asio::executor; typename std::enable_if<std::is_convertible<ExecutionContext&, boost::asio::execution_context&>::value>::type = void]’:
/usr/include/boost/asio/basic_socket.hpp:132:20:   required from ‘boost::asio::basic_socket<Protocol, Executor>::basic_socket(ExecutionContext&, typename std::enable_if<std::is_convertible<ExecutionContext&, boost::asio::execution_context&>::value>::type*) [with ExecutionContext = boost::asio::execution_context; Protocol = boost::asio::ip::tcp; Executor = boost::asio::executor; typename std::enable_if<std::is_convertible<ExecutionContext&, boost::asio::execution_context&>::value>::type = void]’
/usr/include/boost/asio/basic_stream_socket.hpp:112:47:   required from ‘boost::asio::basic_stream_socket<Protocol, Executor>::basic_stream_socket(ExecutionContext&, typename std::enable_if<std::is_convertible<ExecutionContext&, boost::asio::execution_context&>::value>::type*) [with ExecutionContext = boost::asio::execution_context; Protocol = boost::asio::ip::tcp; Executor = boost::asio::executor; typename std::enable_if<std::is_convertible<ExecutionContext&, boost::asio::execution_context&>::value>::type = void]’
src/server.cpp:10:58:   required from here
/usr/include/boost/asio/detail/io_object_impl.hpp:87:40: error: ‘class boost::asio::execution_context’ has no member named ‘get_executor’
   87 |       implementation_executor_(context.get_executor(),
      |                                ~~~~~~~~^~~~~~~~~~~~
In file included from /usr/include/boost/asio/impl/execution_context.hpp:18,
                 from /usr/include/boost/asio/execution_context.hpp:409,
                 from /usr/include/boost/asio/detail/scheduler.hpp:21,
                 from /usr/include/boost/asio/system_context.hpp:19,
                 from /usr/include/boost/asio/impl/system_executor.hpp:22,
                 from /usr/include/boost/asio/system_executor.hpp:129,
                 from /usr/include/boost/asio/associated_executor.hpp:21,
                 from /usr/include/boost/asio.hpp:21,
                 from include/server.h:4,
                 from src/server.cpp:1:
/usr/include/boost/asio/basic_socket_acceptor.hpp: In instantiation of ‘void boost::asio::basic_socket_acceptor<Protocol, Executor>::initiate_async_accept::operator()(AcceptHandler&&, boost::asio::basic_socket<Protocol1, Executor1>*, boost::asio::basic_socket_acceptor<Protocol, Executor>::endpoint_type*) const [with AcceptHandler = Server::start_accept()::<lambda(boost::system::error_code, boost::asio::ip::tcp::socket)>; Protocol1 = boost::asio::ip::tcp; Executor1 = boost::asio::executor; Protocol = boost::asio::ip::tcp; Executor = boost::asio::executor; boost::asio::basic_socket_acceptor<Protocol, Executor>::endpoint_type = boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>]’:
/usr/include/boost/asio/async_result.hpp:152:49:   required from ‘static boost::asio::async_result<CompletionToken, Signature>::return_type boost::asio::async_result<CompletionToken, Signature>::initiate(Initiation&&, RawCompletionToken&&, Args&& ...) [with Initiation = boost::asio::basic_socket_acceptor<boost::asio::ip::tcp>::initiate_async_accept; RawCompletionToken = Server::start_accept()::<lambda(boost::system::error_code, boost::asio::ip::tcp::socket)>; Args = {boost::asio::basic_socket<boost::asio::ip::tcp, boost::asio::executor>*, boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>*}; CompletionToken = Server::start_accept()::<lambda(boost::system::error_code, boost::asio::ip::tcp::socket)>; Signature = void(boost::system::error_code); boost::asio::async_result<CompletionToken, Signature>::return_type = void]’
/usr/include/boost/asio/async_result.hpp:365:25:   required from ‘typename std::enable_if<boost::asio::detail::async_result_has_initiate_memfn<CompletionToken, Signature>::value, decltype (boost::asio::async_result<typename std::decay<_Tp>::type, Signature>::initiate(declval<Initiation&&>(), declval<CompletionToken&&>(), (declval<Args&>)()...))>::type boost::asio::async_initiate(Initiation&&, CompletionToken&, Args&& ...) [with CompletionToken = Server::start_accept()::<lambda(boost::system::error_code, boost::asio::ip::tcp::socket)>; Signature = void(boost::system::error_code); Initiation = boost::asio::basic_socket_acceptor<boost::asio::ip::tcp>::initiate_async_accept; Args = {boost::asio::basic_socket<boost::asio::ip::tcp, boost::asio::executor>*, boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>*}; typename std::enable_if<boost::asio::detail::async_result_has_initiate_memfn<CompletionToken, Signature>::value, decltype (boost::asio::async_result<typename std::decay<_Tp>::type, Signature>::initiate(declval<Initiation&&>(), declval<CompletionToken&&>(), (declval<Args&>)()...))>::type = void]’
/usr/include/boost/asio/basic_socket_acceptor.hpp:1351:75:   required from ‘auto boost::asio::basic_socket_acceptor<Protocol, Executor>::async_accept(boost::asio::basic_socket<Protocol1, Executor1>&, AcceptHandler&&, typename std::enable_if<std::is_convertible<Protocol, Protocol1>::value>::type*) [with Protocol1 = boost::asio::ip::tcp; Executor1 = boost::asio::executor; AcceptHandler = Server::start_accept()::<lambda(boost::system::error_code, boost::asio::ip::tcp::socket)>; Protocol = boost::asio::ip::tcp; Executor = boost::asio::executor; typename std::enable_if<std::is_convertible<Protocol, Protocol1>::value>::type = void]’
src/server.cpp:15:5:   required from here
/usr/include/boost/asio/basic_socket_acceptor.hpp:2434:7: error: static assertion failed: AcceptHandler type requirements not met
 2434 |       BOOST_ASIO_ACCEPT_HANDLER_CHECK(AcceptHandler, handler) type_check;
      |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/boost/asio/basic_socket.hpp:1837:7: error: no match for call to ‘(Server::start_accept()::<lambda(boost::system::error_code, boost::asio::ip::tcp::socket)>) (const boost::system::error_code&)’
 1837 |       BOOST_ASIO_CONNECT_HANDLER_CHECK(ConnectHandler, handler) type_check;
      |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/server.cpp:12:9: note: candidate: ‘Server::start_accept()::<lambda(boost::system::error_code, boost::asio::ip::tcp::socket)>’
   12 |         [this](boost::system::error_code ec, tcp::socket socket) {
      |         ^
src/server.cpp:12:9: note:   candidate expects 2 arguments, 1 provided
In file included from /usr/include/boost/asio/impl/io_context.hpp:22,
                 from /usr/include/boost/asio/io_context.hpp:861,
                 from /usr/include/boost/asio/detail/io_object_executor.hpp:21,
                 from /usr/include/boost/asio/detail/io_object_impl.hpp:20,
                 from /usr/include/boost/asio/basic_socket.hpp:21,
                 from /usr/include/boost/asio/basic_datagram_socket.hpp:20,
                 from /usr/include/boost/asio.hpp:24,
                 from include/server.h:4,
                 from src/server.cpp:1:
/usr/include/boost/asio/detail/non_const_lvalue.hpp:31:12: error: ‘boost::asio::detail::non_const_lvalue<T>::non_const_lvalue(T&) [with T = Server::start_accept()::<lambda(boost::system::error_code, boost::asio::ip::tcp::socket)>]’, declared using local type ‘Server::start_accept()::<lambda(boost::system::error_code, boost::asio::ip::tcp::socket)>’, is used but never defined [-fpermissive]
   31 |   explicit non_const_lvalue(T& t)
      |            ^~~~~~~~~~~~~~~~
In file included from /usr/include/boost/asio/basic_socket.hpp:36,
                 from /usr/include/boost/asio/basic_datagram_socket.hpp:20,
                 from /usr/include/boost/asio.hpp:24,
                 from include/server.h:4,
                 from src/server.cpp:1:
/usr/include/boost/asio/detail/reactive_socket_service.hpp:419:8: error: ‘void boost::asio::detail::reactive_socket_service<Protocol>::async_accept(boost::asio::detail::reactive_socket_service<Protocol>::implementation_type&, Socket&, boost::asio::detail::reactive_socket_service<Protocol>::endpoint_type*, Handler&, const IoExecutor&) [with Socket = boost::asio::basic_socket<boost::asio::ip::tcp, boost::asio::executor>; Handler = Server::start_accept()::<lambda(boost::system::error_code, boost::asio::ip::tcp::socket)>; IoExecutor = boost::asio::detail::io_object_executor<boost::asio::executor>; Protocol = boost::asio::ip::tcp; boost::asio::detail::reactive_socket_service<Protocol>::endpoint_type = boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>]’, declared using local type ‘Server::start_accept()::<lambda(boost::system::error_code, boost::asio::ip::tcp::socket)>’, is used but never defined [-fpermissive]
  419 |   void async_accept(implementation_type& impl, Socket& peer,
      |        ^~~~~~~~~~~~

i had try something like use make_shared, but it don't work...


Solution

  • These interfaces do have changed slightly.

    The good news is, you don't need all that. The idea of passing around execution contexts by reference is anti-pattern (since a long time) now. Instead, pass the executor - by value. It can decouple implementations from the particular type of execution context (system_context, thread_pool, io_context etc).

    So at least change

    tcp::socket socket(acceptor_.get_executor().context());
    

    To

    tcp::socket socket(acceptor_.get_executor());
    

    Other Problems

    You have Undefined Behaviour because you pass a reference to the local socket variable to an async operation.

    You're also mixing overloads, because you are passing the socket by reference, but ALSO using the MoveAcceptHandler completion signature. That should not compile.

    When we fix that, it already works for me with Asio 1.16.1: https://compiler-explorer.com/z/EjvfPPeqr

    void Server::start_accept() {
        acceptor_.async_accept(socket_, [this](error_code ec) { handle_accept(std::move(socket_), ec); });
    }
    

    enter image description here

    Simplify

    Instead, embrace the MoveAcceptHandler! It will already do what you want, namely propagate the acceptor's executor to the new socket object.

    Also removing the unnecessary handle_accept method:

    struct Server {
        Server(asio::executor ex, uint16_t port) : acceptor_(ex, {{}, port}) { accept_loop(); }
    
      private:
        void accept_loop() {
            acceptor_.async_accept([this](error_code error, tcp::socket socket) {
                if (!error)
                    std::cout << "New connection accepted: " << socket.remote_endpoint() << std::endl;
                else
                    std::cerr << "Error on accept: " << error.message() << std::endl;
    
                accept_loop();
            });
        }
    
        tcp::acceptor acceptor_;
    };
    
    int main() {
        std::cout << BOOST_ASIO_VERSION << "\n";
        asio::io_context ioc;
    
        Server s(ioc.get_executor(), 8989);
    
        ioc.run();
    }
    

    enter image description here

    For more recent boost you'd use any_io_executor instead of executor.