81 lines
3.7 KiB
Plaintext
81 lines
3.7 KiB
Plaintext
[/
|
|
Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
|
|
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
Official repository: https://github.com/boostorg/beast
|
|
]
|
|
|
|
[section:echo Echo __example__]
|
|
|
|
This example develops an initiating function called [*echo].
|
|
The operation will read up to the first newline on a stream, and
|
|
then write the same line including the newline back on the stream.
|
|
First we define the input parameters and results, then declare our
|
|
initiation function. For our echo operation the only inputs are the
|
|
stream and the completion token. The output is the error code which
|
|
is usually included in all completion handler signatures.
|
|
|
|
[example_core_echo_op_2]
|
|
|
|
Now that we have a declaration, we will define the body of the function.
|
|
We want to achieve the following goals: perform static type checking on
|
|
the input parameters, set up the return value as per __N3747__, and launch
|
|
the composed operation by constructing an intermediate, stateful completion
|
|
handler and invoking it.
|
|
|
|
The initiating function contains a few relatively simple parts. There is
|
|
the customization of the return value type, static type checking, building
|
|
the return value type using the helper, and creating and launching the
|
|
`echo_op` composed operation object.
|
|
|
|
The implementation strategy is to make the composed object meet the
|
|
requirements of a completion handler by being movable, and by making it
|
|
invocable so it can be used as a continuation for the asynchronous operations
|
|
it launches. Rather than using `std::bind` or `boost::bind`, which destroys
|
|
the type information and therefore breaks the allocation and invocation hooks,
|
|
we will simply pass `std::move(*this)` as the completion handler parameter for
|
|
any operations that we initiate. For the move to work correctly, care must be
|
|
taken to ensure that no access to data members are made after the move takes
|
|
place. Here is the complete implementation of our composed operation:
|
|
|
|
[example_core_echo_op_3]
|
|
|
|
There are some common mistakes that should be avoided when writing
|
|
composed operations:
|
|
|
|
* Type erasing the final handler. This will cause undefined behavior.
|
|
|
|
* Forgetting to include a return statement after calling an
|
|
initiating function.
|
|
|
|
* Calling a synchronous function by accident. In general composed
|
|
operations should not block for long periods of time, since this
|
|
ties up a thread running on the __io_context__.
|
|
|
|
* Forgetting to provide `executor_type` and `get_executor` for the
|
|
composed operation. This will cause undefined behavior. For example,
|
|
if someone calls the initiating function with a strand-wrapped
|
|
function object, and there is more than thread running on the
|
|
__io_context__, the underlying stream may be accessed in a fashion
|
|
that violates safety guarantees. Beast provides class templates
|
|
to take care of this boilerplate for you.
|
|
|
|
* Forgetting to create an object of type __executor_work_guard__ with the
|
|
type of executor returned by the stream's `get_executor` member function.
|
|
|
|
* For operations which complete immediately (i.e. without calling an
|
|
intermediate initiating function), forgetting to use __post__ to
|
|
invoke the final handler. This breaks the following initiating
|
|
function guarantee: ['Regardless of whether the asynchronous operation
|
|
completes immediately or not, the handler will not be invoked from
|
|
within this function. Invocation of the handler will be performed
|
|
in a manner equivalent to using __post__]. The function
|
|
__bind_handler__ is provided for this purpose.
|
|
|
|
The listing for a complete, runnable version of this example is in
|
|
[path_link example/echo-op/echo_op.cpp echo_op.cpp].
|
|
|
|
[endsect]
|