2

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?

kenba
  • 4,303
  • 1
  • 23
  • 40

1 Answers1

5

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();
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Add [Live Demo](http://coliru.stacked-crooked.com/a/20f32bc1948b4d23) – sehe Nov 11 '20 at 15:14
  • 2
    Any chance that you could link to a sample TCP port listener/acceptor that uses this model? This answer is not clear to me at all. – Beirdo Aug 02 '21 at 01:38
  • The original question is quite clear (encountered the very same situation myself), while this does not directly answer the question. It seems to me that Boost basically broke with the old style API (circa v 1.7.0) and forces us all to use the new asynch model, where the io_service has been deeply abstracted away. – Ashot Madatyan Sep 18 '21 at 16:22