| 114 | class DynamicBuffer, |
| 115 | class CompletionToken> |
| 116 | auto |
| 117 | async_echo( |
| 118 | AsyncStream& stream, |
| 119 | DynamicBuffer& buffer, |
| 120 | CompletionToken&& token) -> |
| 121 | typename net::async_result< |
| 122 | typename std::decay<CompletionToken>::type, |
| 123 | void(beast::error_code)>::return_type /*< The completion handler signature goes here >*/ |
| 124 | { |
| 125 | // Perform some type checks using static assert, this helps |
| 126 | // with more friendly error messages when passing the wrong types. |
| 127 | static_assert( |
| 128 | beast::is_async_stream<AsyncStream>::value, |
| 129 | "AsyncStream type requirements not met"); |
| 130 | static_assert( |
| 131 | net::is_dynamic_buffer<DynamicBuffer>::value, |
| 132 | "DynamicBuffer type requirements not met"); |
| 133 | |
| 134 | // This class template deduces the actual handler type from a |
| 135 | // CompletionToken, captures a local reference to the handler, |
| 136 | // and creates the `async_result` object which becomes the |
| 137 | // return value of this initiating function. |
| 138 | |
| 139 | net::async_completion<CompletionToken, void(beast::error_code)> init(token); |
| 140 | |
| 141 | // The helper macro BOOST_ASIO_HANDLER_TYPE converts the completion |
| 142 | // token type into a concrete handler type of the correct signature. |
| 143 | |
| 144 | using handler_type = BOOST_ASIO_HANDLER_TYPE(CompletionToken, void(beast::error_code)); |
| 145 | |
| 146 | // The class template `async_base` holds the caller's completion |
| 147 | // handler for us, and provides all of the boilerplate for forwarding |
| 148 | // the associated allocator and associated executor from the caller's |
| 149 | // handler to our operation. It also maintains a `net::executor_work_guard` |
| 150 | // for the executor associated with the stream. This work guard is |
| 151 | // inexpensive, and prevents the execution context from running out |
| 152 | // of work. It is usually necessary although rarely it can be skipped |
| 153 | // depending on the operation (this echo example needs it because it |
| 154 | // performs more than one asynchronous operation in a row). |
| 155 | // We declare this type alias to make the code easier to read. |
| 156 | |
| 157 | using base_type = beast::async_base< |
| 158 | handler_type, /*< The type of the completion handler obtained from the token >*/ |
| 159 | beast::executor_type<AsyncStream> /*< The type of executor used by the stream to dispatch asynchronous operations >*/ |
| 160 | >; |
| 161 | |
| 162 | // This nested class implements the echo composed operation as a |
| 163 | // stateful completion handler. We derive from `async_base` to |
| 164 | // take care of boilerplate and we derived from asio::coroutine to |
| 165 | // allow the reenter and yield keywords to work. |
| 166 | |
| 167 | struct echo_op : base_type, boost::asio::coroutine |
| 168 | { |
| 169 | AsyncStream& stream_; |
| 170 | DynamicBuffer& buffer_; |
| 171 | |
| 172 | echo_op( |
| 173 | AsyncStream& stream, |