...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
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 Boost.Asio.
While these operations may be written from scratch to conform with the
requirements
on asynchronous operations, Boost.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& socket_; boost::asio::mutable_buffer buffer_; enum { starting, reading, writing } state_; template <typename Self> void operator()(Self& self, boost::system::error_code error = {}, std::size_t n = 0) { switch (state_) { case starting: state_ = reading; socket_.async_read_some( buffer_, std::move(self)); break; case reading: if (error) { self.complete(error, 0); } else { state_ = writing; boost::asio::async_write(socket_, buffer_, boost::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, boost::asio::mutable_buffer buffer, CompletionToken&& token) -> typename boost::asio::async_result< typename std::decay<CompletionToken>::type, void(boost::system::error_code, std::size_t)>::return_type { return boost::asio::async_compose<CompletionToken, void(boost::system::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).