1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980 |
- [/
- 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]
|