Creates an initiation function object that may be used to launch a coroutine-based composed asynchronous operation.
template< completion_signature... Signatures, typename Implementation, typename... IoObjectsOrExecutors> auto co_composed( Implementation && implementation, IoObjectsOrExecutors &&... io_objects_or_executors);
The experimental::co_composed
utility simplifies the
implementation of composed asynchronous operations by automatically adapting
a coroutine to be an initiation function object for use with async_initiate
. When awaiting asynchronous
operations, the coroutine automatically uses a conforming intermediate completion
handler.
A function object that contains the coroutine-based implementation
of the composed asynchronous operation. The first argument to the function
object represents the state of the operation, and may be used to test
for cancellation. The remaining arguments are those passed to async_initiate
after the completion
token.
Zero or more I/O objects or I/O executors for which outstanding work must be maintained while the operation is incomplete.
By default, per-operation cancellation is disabled for composed operations
that use experimental::co_composed
. It must be explicitly
enabled by calling the state's reset_cancellation_state
function.
The following example illustrates manual error handling and explicit checks
for cancellation. The completion handler is invoked via a co_yield
to the state's complete
function,
which never returns.
template <typename CompletionToken> auto async_echo(tcp::socket& socket, CompletionToken&& token) { return asio::async_initiate< CompletionToken, void(std::error_code)>( asio::experimental::co_composed( [](auto state, tcp::socket& socket) -> void { state.reset_cancellation_state( asio::enable_terminal_cancellation()); while (!state.cancelled()) { char data[1024]; auto [e1, n1] = co_await socket.async_read_some( asio::buffer(data), asio::as_tuple(asio::deferred)); if (e1) co_yield state.complete(e1); if (!!state.cancelled()) co_yield state.complete( make_error_code(asio::error::operation_aborted)); auto [e2, n2] = co_await asio::async_write(socket, asio::buffer(data, n1), asio::as_tuple(asio::deferred)); if (e2) co_yield state.complete(e2); } }, socket), token, std::ref(socket)); }
This next example shows exception-based error handling and implicit checks
for cancellation. The completion handler is invoked after returning from
the coroutine via co_return
.
Valid co_return
values are
specified using completion signatures passed to the co_composed
function.
template <typename CompletionToken> auto async_echo(tcp::socket& socket, CompletionToken&& token) { return asio::async_initiate< CompletionToken, void(std::error_code)>( asio::experimental::co_composed< void(std::error_code)>( [](auto state, tcp::socket& socket) -> void { try { state.throw_if_cancelled(true); state.reset_cancellation_state( asio::enable_terminal_cancellation()); for (;;) { char data[1024]; std::size_t n = co_await socket.async_read_some( asio::buffer(data), asio::deferred); co_await asio::async_write(socket, asio::buffer(data, n), asio::deferred); } } catch (const std::system_error& e) { co_return {e.code()}; } }, socket), token, std::ref(socket)); }
Header: asio/experimental/co_composed.hpp
Convenience header: None