Reputation: 29209
In the Asio release notes for Boost 1.80, one finds:
When targeting C++11 and later, spawn() and basic_yield_context are implemented in terms of Boost.Context directly.
Yay! Now we can remove the dependency on Boost.Coroutine, which is not header-only and needs to be compiled (the dependency on Boost.Context still remains when using boost::asio::spawn
).
Now my question is: how do I migrate my code so that boost::asio::spawn
and boost::asio::basic_yield_context
only depend on Boost.Context?
Upvotes: 2
Views: 1074
Reputation: 29209
First off, BOOST_ASIO_DISABLE_BOOST_COROUTINE
must be defined to disable all usages of Boost.Coroutine from within Boost.Asio , in order to avoid linker errors.
With BOOST_ASIO_DISABLE_BOOST_COROUTINE
defined, you will be forced to pass a completion token as the third argument to boost::asio::spawn
. This completion token must provide a handler with signature void handler(std::exception_ptr)
, where the std::exception_ptr
is used to transport exceptions thrown from within the coroutine.
If you simply want exceptions from the coroutine to be discarded (or if it's not possible for exceptions to leak out of the coroutine), you may pass boost::asio::detached
as the spawn
completion token. For example:
boost::asio::spawn(
ioctx,
[](boost::asio::yield_context yield)
{
// Do coroutine stuff
},
boost::asio::detached
);
If you want exceptions to be rethrown by boost::asio::io_context::run
(as was the old behavior without a completion token), you should pass the following handler (or equivalent) as the completion token:
[](std::exception_ptr e) { if (e) std::rethrow_exception(e); }
For example:
boost::asio::spawn(
ioctx,
[](boost::asio::yield_context yield)
{
// Do coroutine stuff
throw std::runtime_error("Oops");
},
[](std::exception_ptr e) { if (e) std::rethrow_exception(e); }
);
Note that there's currently a bug in Boost 1.80 where exceptions thrown from coroutines are not captured properly by boost::asio::spawn
.
Here's a complete example that demonstrates the disabling of Boost.Coroutine, so that it only depends on Boost.Context (and Boost.System):
#define BOOST_ASIO_DISABLE_BOOST_COROUTINE
#include <exception>
#include <iostream>
#include <stdexcept>
#include <boost/asio/io_context.hpp>
#include <boost/asio/spawn.hpp>
//------------------------------------------------------------------------------
struct propagating_t
{
constexpr propagating_t() = default;
void operator()(std::exception_ptr e) const
{
if (e) std::rethrow_exception(e);
}
};
// Our own custom completion token that rethrows exceptions leaked
// from coroutines.
constexpr propagating_t propagating;
//------------------------------------------------------------------------------
int main()
{
boost::asio::io_context ioctx;
boost::asio::spawn(
ioctx,
[](boost::asio::yield_context yield)
{
std::cout << "Coroutine start" << std::endl;
throw std::runtime_error("bad");
},
propagating
);
try
{
ioctx.run();
}
catch (const std::exception& e)
{
std::cout << "Caught exception: " << e.what() << std::endl;
}
catch (...)
{
std::cout << "Caught unknown exception" << std::endl;
}
return 0;
}
Special thanks to Chris Kohlhoff, the author of Asio, who helped me understand this usage of boost::asio::spawn
.
Upvotes: 3