Application developers may wish to package their own compositions as conforming asynchronous operations within the asynchronous model. Doing so facilitates seamless composition of these operations together with the operations already provided by Asio.
While these operations may be written from scratch to conform with the
requirements on
asynchronous operations, Asio includes the async_compose
function to simplify
this process. The async_compose
implementation automatically provides an intermediate completion handler
that correctly propagates the associated
characteristics and tracks outstanding work against the I/O executor
and completion executor.
The following example illustrates an asynchronous echo loop (i.e. read, followed by write, and so on), expressed as a simple state machine.
struct async_echo_implementation { tcp::socket& local_socket; asio::mutable_buffer buffer_; enum { starting, reading, writing } state_; template <typename Self> void operator()(Self& self, asio::error_code error = {}, std::size_t n = 0) { switch (state_) { case starting: state_ = reading; local_socket.async_read_some( buffer_, std::move(self)); break; case reading: if (error) { self.complete(error, 0); } else { state_ = writing; asio::async_write(local_socket, buffer_, asio::transfer_exactly(n), std::move(self)); } break; case writing: self.complete(error, n); break; } } };
This implementation is then used in an initiating function, which trivially
wraps async_compose
:
template <typename CompletionToken> auto async_echo(tcp::socket& socket, asio::mutable_buffer buffer, CompletionToken&& token) -> typename asio::async_result< typename std::decay<CompletionToken>::type, void(asio::error_code, std::size_t)>::return_type { return asio::async_compose<CompletionToken, void(asio::error_code, std::size_t)>( async_echo_implementation{socket, buffer, async_echo_implementation::starting}, token, socket); }
Here, async_compose
is
first passed the function object that contains the implementation of the
composed asynchronous operation. The first argument to the function object
is a non-const reference to the enclosing intermediate completion handler.
The remaining arguments are any arguments that originate from the completion
handlers of any asynchronous operations performed by the implementation.
The async_compose
function
is also passed the completion token, and zero or more I/O objects or I/O
executors for which outstanding work must be maintained.
async_compose, Operations examples (C++11), Operations examples (C++14).