Remove extraneous doc file

This commit is contained in:
Vinnie Falco
2017-06-10 16:00:15 -07:00
parent 78f4858e98
commit 8f88f9c3bb

View File

@@ -1,725 +0,0 @@
[/
Copyright (c) 2013-2017 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)
]
[section:core Core Concepts]
[block '''
<informaltable frame="all"><tgroup cols="1"><colspec colname="a"/><tbody><row><entry valign="top"><simplelist>
<member><link linkend="beast.core.asio">Working With Asio</link></member>
<member><link linkend="beast.core.streams">Stream Concepts</link></member>
<member><link linkend="beast.core.buffers">Buffer Concepts</link></member>
<member><link linkend="beast.core.async">Asynchronous Utilities</link></member>
<member><link linkend="beast.core.tutorial">Asynchronous Composed Operation Tutorial</link></member>
</simplelist></entry></row></tbody></tgroup></informaltable>
''']
A goal of the library is expose implementation primitives in order that
users may build their own library-like components. These primitives include
traits, buffers, buffer algorithms, and helpers for implementing asynchronous
operations compatible with __Asio__ and described in __N3747__. This section
lists these facilities by group, with descriptions.
[important
This documentation assumes familiarity with __Asio__. Sample
code and identifiers used throughout are written as if the
following declarations are in effect:
```
#include <beast/core.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <thread>
using namespace beast;
boost::asio::io_service ios;
boost::asio::io_service work{ios};
std::thread t{[&](){ ios.run(); }};
error_code ec;
```
]
[heading:asio Working With Asio]
Beast does not manage sockets, make outgoing connections, accept incoming
connections, or handle any aspect of connection management. In order to
invoke library algorithms it is necessary to first have a connected socket,
SSL stream, or other object which meets the required stream concepts. This
example is provided as a reminder of how to work with sockets:
```
auto host = "www.example.com";
boost::asio::ip::tcp::resolver r{ios};
boost::asio::ip::tcp::socket sock{ios};
boost::asio::connect(sock, r.resolve(
boost::asio::ip::tcp::resolver::query{host, "http"}));
// At this point `sock` is a connected to a remote
// host and may be used to perform stream operations.
```
Throughout this documentation identifiers with the following names have
special meaning:
[table Global Variables
[[Name][Description]]
[[
[@http://www.boost.org/doc/html/boost_asio/reference/io_service.html [*`ios`]]
][
A variable of type
[@http://www.boost.org/doc/html/boost_asio/reference/io_service.html `boost::asio::io_service`]
which is running on one separate thread, and upon which a
[@http://www.boost.org/doc/html/boost_asio/reference/io_service__work.html `boost::asio::io_service::work`]
object has been constructed.
]]
[[
[@http://www.boost.org/doc/html/boost_asio/reference/ip__tcp/socket.html [*`sock`]]
][
A variable of type
[@http://www.boost.org/doc/html/boost_asio/reference/ip__tcp/socket.html `boost::asio::ip::tcp::socket`]
which has already been connected to a remote host.
]]
[[
[@http://www.boost.org/doc/html/boost_asio/reference/ssl__stream.html [*`ssl_sock`]]
][
A variable of type
[@http://www.boost.org/doc/html/boost_asio/reference/ssl__stream.html `boost::asio::ssl::stream<boost::asio::ip::tcp::socket>`]
which is already connected and has handshaked with a remote host.
]]
]
[heading:streams Stream Concepts]
A __Stream__ is communication channel where data expressed as octet
buffers is transferred sequentially. Streams are either synchronous
or asynchronous, and may allow reading, writing, or both. Note that
a particular type may model more than one concept. For example, the
Asio types
[@http://www.boost.org/doc/html/boost_asio/reference/ip__tcp/socket.html `boost::asio::ip::tcp::socket`]
and
[@http://www.boost.org/doc/html/boost_asio/reference/ssl__stream.html `boost::asio::ssl::stream`]
support everything. All stream algorithms in Beast are declared as
template functions with specific concept requirements chosen from
this list:
[table Stream Concepts
[[Concept][Description]]
[
[__SyncReadStream__]
[
Supports buffer-oriented blocking reads.
]
][
[__SyncWriteStream__]
[
Supports buffer-oriented blocking writes.
]
][
[__SyncStream__]
[
A stream supporting buffer-oriented blocking reads and writes.
]
][
[__AsyncReadStream__]
[
Supports buffer-oriented asynchronous reads.
]
][
[__AsyncWriteStream__]
[
Supports buffer-oriented asynchronous writes.
]
][
[__AsyncStream__]
[
A stream supporting buffer-oriented asynchronous reads and writes.
]
]
]
These template metafunctions check whether a given type meets the
requirements for the various stream concepts, and some additional
useful utilities. The library uses these type checks internally
and also provides them as public interfaces so users may use the
same techniques to augment their own code. The use of these type
checks helps provide more concise errors during compilation:
[table Stream Type Checks
[[Name][Description]]
[[
[link beast.ref.get_lowest_layer `get_lowest_layer`]
][
Returns `T::lowest_layer_type` if it exists, else returns `T`.
]]
[[
[link beast.ref.has_get_io_service `has_get_io_service`]
][
Determine if the `get_io_service` member function is present,
and returns an __io_service__.
]]
[[
[link beast.ref.is_async_read_stream `is_async_read_stream`]
][
Determine if a type meets the requirements of __AsyncReadStream__.
]]
[[
[link beast.ref.is_async_stream `is_async_stream`]
][
Determine if a type meets the requirements of both __AsyncReadStream__
and __AsyncWriteStream__.
]]
[[
[link beast.ref.is_async_write_stream `is_async_write_stream`]
][
Determine if a type meets the requirements of __AsyncWriteStream__.
]]
[[
[link beast.ref.is_completion_handler `is_completion_handler`]
][
Determine if a type meets the requirements of __CompletionHandler__,
and is callable with a specified signature.
]]
[[
[link beast.ref.is_sync_read_stream `is_sync_read_stream`]
][
Determine if a type meets the requirements of __SyncReadStream__.
]]
[[
[link beast.ref.is_sync_stream `is_sync_stream`]
][
Determine if a type meets the requirements of both __SyncReadStream__
and __SyncWriteStream__.
]]
[[
[link beast.ref.is_sync_write_stream `is_sync_write_stream`]
][
Determine if a type meets the requirements of __SyncWriteStream__.
]]
]
Using the type checks with `static_assert` on function or class template
types will provide users with helpful error messages and prevent undefined
behaviors. This example shows how a template function which writes to a
synchronous stream may check its argument:
```
template<class SyncWriteStream>
void write_string(SyncWriteStream& stream, string_view s)
{
static_assert(is_sync_write_stream<SyncWriteStream>::value,
"SyncWriteStream requirements not met");
boost::asio::write(stream, boost::asio::const_buffers_1(s.data(), s.size()));
}
```
[heading:buffers Buffer Concepts]
__Asio__ provides the __ConstBufferSequence__ and __MutableBufferSequence__
concepts, whose models provide ranges of buffers, as well as the __streambuf__
class which encapsulates memory storage that may be automatically resized as
required, where the memory is divided into an input sequence followed by an
output sequence. The Networking TS (__N4588__) generalizes the `streambuf`
interface into the __DynamicBuffer__ concept. Beast algorithms which require
resizable buffers accept as parameters dynamic buffer objects. These
metafunctions check types against these buffer concepts:
[table Buffer Type Checks
[[Name][Description]]
[[
[link beast.ref.is_const_buffer_sequence `is_const_buffer_sequence`]
][
Determine if a type meets the requirements of __ConstBufferSequence__.
]]
[[
[link beast.ref.is_mutable_buffer_sequence `is_mutable_buffer_sequence`]
][
Determine if a type meets the requirements of __MutableBufferSequence__.
]]
[[
[link beast.ref.is_dynamic_buffer `is_dynamic_buffer`]
][
Determine if a type meets the requirements of __DynamicBuffer__.
]]
]
To suit various needs, several implementation of dynamic buffer are available:
[table Dynamic Buffer Implementations
[[Name][Description]]
[[
[link beast.ref.buffers_adapter `buffers_adapter`]
][
This wrapper adapts any __MutableBufferSequence__ into a
__DynamicBuffer__ with an upper limit on the total size of the input and
output areas equal to the size of the underlying mutable buffer sequence.
The implementation does not perform heap allocations.
]]
[[
[link beast.ref.flat_buffer `flat_buffer`]
[link beast.ref.basic_flat_buffer `basic_flat_buffer`]
][
Guarantees that input and output areas are buffer sequences with
length one. Upon construction an optional upper limit to the total
size of the input and output areas may be set. The basic container
supports the standard allocator model.
]]
[[
[link beast.ref.multi_buffer `multi_buffer`]
[link beast.ref.basic_multi_buffer `basic_multi_buffer`]
][
Uses a sequence of one or more character arrays of varying sizes.
Additional character array objects are appended to the sequence to
accommodate changes in the size of the character sequence. The basic
container supports the standard allocator model.
]]
[[
[link beast.ref.static_buffer `static_buffer`]
[link beast.ref.static_buffer `static_buffer_n`]
][
Provides the facilities of a dynamic buffer, subject to an upper
limit placed on the total size of the input and output areas defined
by a constexpr template parameter. The storage for the sequences are
kept in the class; the implementation does not perform heap allocations.
]]
]
Network applications frequently need to manipulate buffer sequences. To
facilitate working with buffers the library treats these sequences as
a special type of range. Algorithms and wrappers are provided which
transform these buffer sequences ranges efficiently using lazy evaluation.
No memory allocations are used in the transformations; instead, they
create lightweight iterators over the existing, unmodified memory
buffers. The lifetime of the transformed buffers is retained by the
caller; ownership is not transferred.
[table Buffer Algorithms
[[Name][Description]]
[[
[link beast.ref.buffer_cat `buffer_cat`]
][
This functions returns a new buffer sequence which, when iterated,
traverses the sequence which would be formed if all of the input buffer
sequences were concatenated. With this routine, multiple calls to a
stream's `write_some` function may be combined into one, eliminating
expensive system calls.
]]
[[
[link beast.ref.buffer_cat_view `buffer_cat_view`]
][
This class represents the buffer sequence formed by concatenating
two or more buffer sequences. This is type of object returned by
[link beast.ref.buffer_cat `buffer_cat`].
]]
[[
[link beast.ref.buffer_prefix `buffer_prefix`]
][
This function returns a new buffer or buffer sequence which represents
a prefix of the original buffers.
]]
[[
[link beast.ref.buffer_prefix_view `buffer_prefix_view`]
][
This class represents the buffer sequence formed from a prefix of
an existing buffer sequence. This is the type of buffer returned by
[link beast.ref.buffer_prefix `buffer_prefix`].
]]
[[
[link beast.ref.consuming_buffers `consuming_buffers`]
][
This class wraps the underlying memory of an existing buffer sequence
and presents a suffix of the original sequence. The length of the suffix
may be progressively shortened. This lets callers work with sequential
increments of a buffer sequence.
]]
]
These two functions facilitate buffer interoperability with standard
output streams.
[table Buffer Output Streams
[[Name][Description]]
[[
[link beast.ref.buffers `buffers`]
][
This function wraps a __ConstBufferSequence__ so it may be
used with `operator<<` and `std::ostream`.
]]
[[
[link beast.ref.ostream `ostream`]
][
This function returns a `std::ostream` which wraps a dynamic buffer.
Characters sent to the stream using `operator<<` is stored in the
dynamic buffer.
]]
]
[heading:async Asynchronous Utilities]
Asynchronous operations are started by calling a free function or member
function known as an ['asynchronous initiation function]. The initiation
function accepts parameters specific to the operation as well as a "completion
token." This token is either a completion handler, or another type allowing for
customization of how the result of the asynchronous operation is conveyed to
callers. __Asio__ allows the special completion tokens __use_future__ and
objects of type __yield_context__ to allow callers to specify the use of futures
and coroutines respectively. This system, where the return value and method of
indicating completion may be customize at the call site of the asynchronous
initiation function, is known as the ['Extensible Asynchronous Model] described
in __N3747__, and built-in to __N4588__.
[note
A full explanation of completion handlers, the Extensible Asynchronous
Model and how these asynchronous interfaces are used is beyond the
scope of this document. Interested readers should consult the
__Asio__ documentation.
]
Since the interfaces provided here are low level, authors of libraries
may wish to create higher level interfaces using the primitives found
in this library. Non-trivial applications will want to provide their own
asynchronous initiation functions which perform a series of other,
intermediate asynchronous operations before invoking the final completion
handler. The set of intermediate actions produced by calling an initiation
function is known as a ['composed operation]. To ensure full interoperability
and well-defined behavior, __Asio__ imposes requirements on the implementation
of composed operations. A number of useful classes and macros to facilitate
the development of composed operations and the associated asynchronous
initiation functions used to launch them are available:
[table Asynchronous Helpers
[[Name][Description]]
[[
[link beast.ref.async_completion `async_completion`]
][
This class aggregates the completion handler customization point and
the asynchronous initiation function return value customization point
into a single object which exposes the appropriate output types for the
given input types, and also contains boilerplate that is necessary to
implement an initiation function using the Extensible Model.
]]
[[
[link beast.ref.async_return_type `async_return_type`]
][
This template alias determines the return value of an asynchronous
initiation function given the completion token and signature. It is used
by asynchronous initiation functions to meet the requirements of the
Extensible Asynchronous Model.
]]
[[
[link beast.ref.bind_handler `bind_handler`]
][
This function returns a new, nullary completion handler which when
invoked with no arguments invokes the original completion handler with a
list of bound arguments. The invocation is made from the same implicit
or explicit strand as that which would be used to invoke the original
handler. This is accomplished by using the correct overload of
`asio_handler_invoke` associated with the original completion handler.
]]
[[
[link beast.ref.handler_alloc `handler_alloc`]
][
This class meets the requirements of [*Allocator], and uses any custom
memory allocation and deallocation hooks associated with a given handler.
It is useful for when a composed operation requires temporary dynamic
allocations to achieve its result. Memory allocated using this allocator
must be freed before the final completion handler is invoked.
]]
[[
[link beast.ref.handler_ptr `handler_ptr`]
][
This is a smart pointer container used to manage the internal state of a
composed operation. It is useful when the state is non trivial. For example
when the state has non-copyable or expensive to copy types. The container
takes ownership of the final completion handler, and provides boilerplate
to invoke the final handler in a way that also deletes the internal state.
The internal state is allocated using the final completion handler's
associated allocator, benefiting from all handler memory management
optimizations transparently.
]]
[[
[link beast.ref.handler_type `handler_type`]
][
This template alias converts a completion token and signature to the
correct completion handler type. It is used in the implementation of
asynchronous initiation functions to meet the requirements of the
Extensible Asynchronous Model.
]]
]
[section:tutorial Asynchronous Composed Operation Tutorial]
To illustrate the usage of the asynchronous helpers in the core section of
this library, we will develop a simple asynchronous composed operation called
[*echo]. This 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.
```
#include <beast/core.hpp>
#include <boost/asio.hpp>
#include <cstddef>
#include <memory>
#include <utility>
// Read a line and echo it back
//
template<class AsyncStream, class CompletionToken>
beast::async_return_type<CompletionToken, void(beast::error_code)>
async_echo(AsyncStream& stream, CompletionToken&& token)
```
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 the object and invoking it.
```
template<class AsyncStream, class Handler>
class echo_op; // This is our composed operation implementation
// Read a line and echo it back
//
template<class AsyncStream, class CompletionToken>
beast::async_return_type<CompletionToken, void(beast::error_code)>
async_echo(AsyncStream& stream, CompletionToken&& token)
{
// Make sure stream meets the requirements. We use static_assert
// to cause a friendly message instead of an error novel.
//
static_assert(beast::is_async_stream<AsyncStream>::value,
"AsyncStream requirements not met");
// This helper manages some of the handler's lifetime and
// uses the result and handler specializations associated with
// the completion token to help customize the return value.
//
beast::async_completion<CompletionToken, void(beast::error_code)> init{token};
// Create the composed operation and launch it. This is a constructor
// call followed by invocation of operator(). We use handler_type
// to convert the completion token into the correct handler type,
// allowing user defined specializations of the async result template
// to take effect.
//
echo_op<AsyncStream, beast::handler_type<CompletionToken, void(beast::error_code)>>{
stream, init.completion_handler}(beast::error_code{}, 0);
// This hook lets the caller see a return value when appropriate.
// For example this might return std::future<error_code> if
// CompletionToken is boost::asio::use_future, or this might
// return an error code if CompletionToken specifies a coroutine.
//
return init.result.get();
}
```
The initiation 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 composed
operation object. The [*`echo_op`] object does most of the work here, and has
a somewhat non-trivial structure. This structure is necessary to meet the
stringent requirements of composed operations (described in more detail in
the __Asio__ documentation). We will touch on these requirements without
explaining them in depth.
First we will create boilerplate which is present in all composed operations
written in this style:
```
// This composed operation reads a line of input and echoes it back.
//
template<class AsyncStream, class Handler>
class echo_op
{
// This holds all of the state information required by the operation.
struct state
{
// The stream to read and write to
AsyncStream& stream;
// Indicates what step in the operation's state machine
// to perform next, starting from zero.
int step = 0;
// The buffer used to hold the input and output data.
// Note that we use a custom allocator for performance,
// this allows the implementation of the io_service to
// make efficient re-use of memory allocated by composed
// operations during continuations.
//
boost::asio::basic_streambuf<beast::handler_alloc<char, Handler>> buffer;
// handler_ptr requires that the first parameter to the
// contained object constructor is a reference to the
// managed final completion handler.
//
explicit state(Handler& handler, AsyncStream& stream_)
: stream(stream_)
, buffer((std::numeric_limits<std::size_t>::max)(),
beast::handler_alloc<char, Handler>{handler})
{
}
};
// This smart pointer container allocates our state using the
// memory allocation hooks associated with the final completion
// handler, manages the lifetime of that handler for us, and
// enforces the destroy-before-invocation requirement on memory
// allocated using the hooks.
//
beast::handler_ptr<state, Handler> p_;
public:
// Boost.Asio requires that handlers are CopyConstructible.
// In some cases, it takes advantage of handlers that are
// MoveConstructible. This operation supports both.
//
echo_op(echo_op&&) = default;
echo_op(echo_op const&) = default;
// The constructor simply creates our state variables in
// the smart pointer container.
//
template<class DeducedHandler, class... Args>
echo_op(AsyncStream& stream, DeducedHandler&& handler)
: p_(std::forward<DeducedHandler>(handler), stream)
{
}
// Determines if the next asynchronous operation represents a
// continuation of the asynchronous flow of control associated
// with the final handler. If we are past step one, it means
// we have performed an asynchronous operation therefore any
// subsequent operation would represent a continuation.
// Otherwise, we propagate the handler's associated value of
// is_continuation. Getting this right means the implementation
// may schedule the invokation of the invoked functions more
// efficiently.
//
friend bool asio_handler_is_continuation(echo_op* op)
{
// This next call is structured to permit argument
// dependent lookup to take effect.
using boost::asio::asio_handler_is_continuation;
// Always use std::addressof to pass the pointer to the handler,
// otherwise an unwanted overload of operator& may be called instead.
return op->p_->step > 1 ||
asio_handler_is_continuation(std::addressof(op->p_.handler()));
}
// Handler hook forwarding. These free functions invoke the hooks
// associated with the final completion handler. In effect, they
// make the Asio implementation treat our composed operation the
// same way it would treat the final completion handler for the
// purpose of memory allocation and invocation.
//
// Our implementation just passes through the call to the hook
// associated with the final handler.
friend void* asio_handler_allocate(std::size_t size, echo_op* op)
{
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(size, std::addressof(op->p_.handler()));
}
friend void asio_handler_deallocate(void* p, std::size_t size, echo_op* op)
{
using boost::asio::asio_handler_deallocate;
return asio_handler_deallocate(p, size, std::addressof(op->p_.handler()));
}
template<class Function>
friend void asio_handler_invoke(Function&& f, echo_op* op)
{
using boost::asio::asio_handler_invoke;
return asio_handler_invoke(f, std::addressof(op->p_.handler()));
}
// Our main entry point. This will get called as our
// intermediate operations complete. Definition below.
//
void operator()(beast::error_code ec, std::size_t bytes_transferred);
};
```
We have the common boilerplate for a composed operation and now we just need
to implement the function call operator. Our strategy is to make our composed
object meet the requirements of a completion handler by being copyable (also
movable), and by providing the function call operator with the correct
signature. 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 implementation of the function call operator for
this echo operation:
```
// We are callable with the signature void(error_code, bytes_transferred),
// allowing `*this` to be used as both a ReadHandler and a WriteHandler.
//
template<class AsyncStream, class Handler>
void echo_op<AsyncStream, Handler>::
operator()(beast::error_code ec, std::size_t bytes_transferred)
{
// Store a reference to our state. The address of the state won't
// change, and this solves the problem where dereferencing the
// data member is undefined after a move.
auto& p = *p_;
// Now perform the next step in the state machine
switch(ec ? 2 : p.step)
{
// initial entry
case 0:
// read up to the first newline
p.step = 1;
return boost::asio::async_read_until(p.stream, p.buffer, "\n", std::move(*this));
case 1:
// write everything back
p.step = 2;
// async_read_until could have read past the newline,
// use buffer_prefix to make sure we only send one line
return boost::asio::async_write(p.stream,
beast::buffer_prefix(bytes_transferred, p.buffer.data()), std::move(*this));
case 2:
p.buffer.consume(bytes_transferred);
break;
}
// Invoke the final handler. If we wanted to pass any arguments
// which come from our state, they would have to be moved to the
// stack first, since the `handler_ptr` guarantees that the state
// is destroyed before the handler is invoked.
//
p_.invoke(ec);
return;
}
```
A complete, runnable version of this example may be found in the examples
directory.
[endsect]
[endsect]