Search code examples
c++c++11boostboost-asio

Boost 1.70 io_service deprecation


I'm trying to migrate some old code from using io_service to io_context for the basic tcp acceptor, but am running into issues when switching get_io_service() to get_executor().context() results in the following error:

cannot convert ‘boost::asio::execution_context’ to ‘boost::asio::io_context&’

This is the listener:

ImageServerListener::ImageServerListener(boost::asio::io_context& io)
{
    _acceptor = new boost::asio::ip::tcp::acceptor(io, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), sConfig.net.imageServerPort));
    StartAccept();
}

ImageServerListener::~ImageServerListener()
{
    delete _acceptor;
}

void ImageServerListener::StartAccept()
{
    std::shared_ptr<ImageServerConnection> connection = ImageServerConnection::create(_acceptor->get_executor().context());
    _acceptor->async_accept(connection->socket(), std::bind(&ImageServerListener::HandleAccept, this, connection));
}

void ImageServerListener::HandleAccept(std::shared_ptr<ImageServerConnection> connection)
{
    connection->Process();
    StartAccept();
}

What would have to be changed in order to return an io_context instead of an execution_context?


Solution

  • You will want to focus on executors rather than contexts.

    Passing around executors is cheap, they are copyable, as opposed to contexts.

    Also, it abstracts away (polymorphism) the type of execution context that the executor is attached to, so you don't need to bother.

    However, the static type of the executor is not fixed. This means that the typical way to accept one is by template argument:

    struct MyThing {
        template <typename Executor>
        explicit MyThing(Executor ex)
           : m_socket(ex)
        { }
    
        void do_stuff(std::string caption) {
            post(m_socket.get_executor(),
                [=] { std::cout << ("Doing stuff " + caption + "\n") << std::flush; });
        }
    
        // ...
      private:
        tcp::socket m_socket;
    };
    

    Now you employ it in many ways without changes:

    Live On Coliru

    int main() {
        boost::asio::thread_pool pool;
        MyThing a(pool.get_executor());
        MyThing b(make_strand(pool));
    
        a.do_stuff("Pool a");
        b.do_stuff("Pool b");
    
        boost::asio::io_context ioc;
        MyThing c(ioc.get_executor());
        MyThing d(make_strand(ioc));
    
        c.do_stuff("IO c");
        d.do_stuff("IO d");
    
        pool.join();
        ioc.run();
    }
    

    Which will print something like

    Doing stuff Pool a
    Doing stuff Pool b
    Doing stuff IO c
    Doing stuff IO d
    

    Type Erasure

    As you have probably surmised, there's type erasure inside m_socket that stores the executor. If you want to do the same, you can use

    boost::asio::any_io_executor ex;
    ex = m_socket.get_executor();