Doc. no: N4478 Date: 2015-04-13 Revises: N4370 Reply-To: Christopher Kohlhoff <chris@kohlhoff.com>
In the June 2014 committee meeting in Rapperswil, LEWG requested that Boost.Asio-based N2175 Networking Library Proposal for TR2 (Revision 1) be updated for C++14 and brought forward as a proposed Networking Technical Specification. This document is that revision. As well as updating the proposal for C++14, it incorporates improvements to Asio that are based on the widespread field experience accumulated since 2007.
The Boost.Asio library, from which this proposal is derived, has been deployed in numerous systems, from large (including internet-facing HTTP servers, instant messaging gateways and financial markets applications) to small (mobile phones and embedded systems). The Asio library supports, or has been ported to, many operating systems including Linux, Mac OS X, Windows (native), Windows Runtime, Solaris, FreeBSD, NetBSD, OpenBSD, HP-UX, Tru64, AIX, iOS, Android, WinCE, Symbian, vxWorks and QNX Neutrino.
This revision contains numerous changes resulting from the LWG wording review held in Cologne in February 2015. Please note, however, it does not yet contain all of the wording corrections and suggestions from that meeting, nor have the applied corrections and changes been reviewed. As the changes are too extensive to list here, readers may wish to view the GitHub page for this proposal at https://github.com/chriskohlhoff/asio-tr2/ for further information.
The purpose of this revision is to provide LEWG with up-to-date wording in order to confirm and/or consider certain design choices, based on feedback from Cologne. To facilitate this process, this revision contains some of the suggested design changes from the wording review. There are two significant design changes worth noting here.
First, the basic_socket_acceptor
class's accept
and async_accept
member functions have been changed
to produce the incoming socket object by moving the result, rather than by
taking a socket object by non-const reference. For example, a synchronous accept
operation may now look like this:
tcp::socket socket(my_acceptor.accept()); // read and write on newly accepted socket ...
and an asynchronous accept operation like this:
my_acceptor.async_accept( [](error_code ec, tcp::socket socket) { if (!ec) { // read and write on newly accepted socket } });
As a consequence of this change, the basic_socket_streambuf
class no longer inherits from basic_socket
— it did so previously to allow users to pass it to accept()
or async_accept()
. Instead, basic_socket_streambuf
and basic_socket_iostream
are
move-enabled and support the ability to move-construct from a socket:
tcp::iostream s(my_acceptor.accept());
Second, there was concern about the use of resource-owning iterators associated
with the ip::basic_resolver
template, and the related
single iterator overloads of the free functions connect
and async_connect
. The resolver
has been changed so that its resolve
operations now return a results class based on the regular expression library's
match_results
template. This
ip::basic_resolver_results
type satisfies the
requirements of a sequence container, except that only the operations defined
for const-qualified sequence containers are supported:
for (auto& result : my_resolver.resolve("www.boost.org", "http")) { std::cout << result->endpoint() << ", "; std::cout << result->host_name() << ", "; std::cout << result->service_name() << std::endl; }
In addition, the single iterator overloads of the free functions connect
and async_connect
have been removed and replaced with overloads that accept an EndpointSequence
. This ensures that the following
simple usage idiom is still supported:
connect(my_socket, my_resolver.resolve("www.boost.org", "http"));
An almost complete implementation of the proposal text may be found in a variant of Asio that stands alone from Boost. This variant is available at https://github.com/chriskohlhoff/asio/tree/master.
Unfamiliar readers are encouraged to look to the Boost.Asio documentation and examples for a more complete picture of the use of the library.
However, to give some idea of the flavour of the proposed library, consider the following sample code. This is part of a server program that echoes the characters it receives back to the client in upper case.
template <typename Iterator> void uppercase(Iterator begin, Iterator end) { std::locale loc(""); for (Iterator iter = begin; iter != end; ++iter) *iter = std::toupper(*iter, loc); } void sync_connection(tcp::socket& socket) { try { std::vector<char> buffer_space(1024); for (;;) { std::size_t length = socket.read_some(buffer(buffer_space)); uppercase(buffer_space.begin(), buffer_space.begin() + length); write(socket, buffer(buffer_space, length)); } } catch (std::system_error& e) { // ... } }
The synchronous operations used above are functions that do not return control to the caller until the corresponding operating system operation completes. In Asio-based programs their use cases typically fall into two categories:
Next, the equivalent code developed using asynchronous operations might look something like this:
class async_connection : public std::enable_shared_from_this<async_connection> { public: async_connection(tcp::socket socket) : socket_(std::move(socket)) { } void start() { do_read(); } private: void do_read() { auto self(shared_from_this()); socket_.async_read_some(buffer(buffer_space_), [this, self](std::error_code ec, std::size_t length) { if (!ec) { uppercase(buffer_space_.begin(), buffer_space_.begin() + length); do_write(length); } }); } void do_write(std::size_t length) { auto self(shared_from_this()); async_write(socket_, buffer(buffer_space_, length), [this, self](std::error_code ec, std::size_t /*length*/) { if (!ec) { do_read(); } }); } tcp::socket socket_; std::vector<char> buffer_space_{1024}; };
Asynchronous operations do not block the caller, but instead involve the delivery of a notification to the program when the corresponding operating system operation completes. Most non-trivial Asio-based programs will make use of asynchronous operations.
While the code may appear more complex due to the inverted flow of control, it allows a knowledgeable programmer to write code that will scale to a great many concurrent connections. However, this proposal uses the asynchronous model described in [N4045]. This is an extensible model that allows the asynchronous operations to support a variety of composition and notification mechanisms, and these mechanisms may alleviate this complexity. This includes futures:
std::future<std::size_t> fut = socket.async_read_some(buffer(buffer_space), use_future); // ... std::size_t length = fut.get();
and, through library extensions, coroutines:
void coro_connection(tcp::socket& socket, yield_context yield) { try { std::vector<char> buffer_space(1024); for (;;) { std::size_t length = socket.async_read_some(buffer(buffer_space), yield); uppercase(buffer_space.begin(), buffer_space.begin() + length); async_write(socket, buffer(buffer_space, length), yield); } } catch (std::system_error& e) { // ... } }
Finally, for many applications, networking is not a core feature, nor is it seen as a core competency of the application’s programmers. To cater to these use cases, the proposal provides a high-level interface to TCP sockets that is designed around the familiar C++ I/O streams framework.
Using the library in this way is as easy as opening a stream object with the remote host’s details:
tcp::iostream s("www.boost.org", "http");
Once connected, you send and receive any data as needed. In this case you send a request:
s << "GET / HTTP/1.0\r\n"; s << "Host: www.boost.org\r\n"; s << "Accept: */*\r\n"; s << "Connection: close\r\n\r\n";
Then receive and process the response:
std::string header; while (std::getline(s, header) && header != "\r") std::cout << header << "\n"; std::cout << s.rdbuf();
You can set a timeout to detect unresponsive connections:
s.expires_after(std::chrono::seconds(60));
And, if at any time there is an error, the tcp::iostream
class’s error()
member function may be used to obtain an error_code
that identifies the reason for failure:
if (!s) { std::cout << "Unable to connect: " << s.error().message() << "\n"; return 1; }
Problem areas addressed by this proposal include:
Features that are considered outside the scope of this proposal include:
The bulk of the library interface is intended for use by developers with at least some understanding of networking concepts (or a willingness to learn). A high level iostreams interface supports simple use cases and permits novices to develop network code without needing to get into too much depth.
The interface is based on the BSD sockets API, which is widely implemented and supported by extensive literature. It is also used as the basis of networking APIs in other languages (e.g. Java). Unsafe practices of the BSD sockets API, e.g. lack of compile-time type safety, are not included.
Asynchronous support is derived from the Proactor design pattern as implemented by the ADAPTIVE Communication Environment [ACE], and is influenced by the design of the Symbian C++ sockets API [SYMBIAN], which supports synchronous and asynchronous operations side-by-side. The Microsoft .NET socket classes [MS-NET] and the Extended Sockets API [ES-API] developed by The Open Group support similar styles of network programming.
This is a pure library proposal. It does not add any new language features, nor does it alter any existing standard library headers. It makes additions to experimental headers that may also be modified by other Technical Specifications.
This library can be implemented using compilers that conform to the C++14 standard. An implementation of this library requires operating system-specific functions that lie outside the C++14 standard.
The asynchronous operations defined in this proposal use the asynchronous model described in [N4045]. With the extensible asynchronous model presented in that paper, the user has the ability to select an asynchronous approach that is appropriate to each use case. With these library foundations, a single extensible asynchronous model can support a variety of composition methods, including:
To facilitate the coordination of asynchronous operations in multithreaded programs, the asynchronous model also utilises the executors design described in [N4242].
As executors and the extensible asynchronous model are a prerequisite for the networking library, the proposed text below incorporates a complete specification of these facilities.
<experimental/executor>
synopsiscompletion_handler_type
async_result
async_completion
associated_allocator
get_associated_allocator
execution_context
execution_context::service
is_executor
uses_executor
associated_executor
get_associated_executor
executor_wrapper
executor_wrapper
constructorsexecutor_wrapper
accessexecutor_wrapper
invocationasync_result
associated_allocator
associated_executor
wrap
executor_work
make_work
system_executor
bad_executor
executor
dispatch
post
defer
strand
use_future_t
async_result
for packaged_task
packaged_handler
packaged_token
package
<experimental/buffer>
synopsismutable_buffer
const_buffer
mutable_buffers_1
const_buffers_1
buffer_cast
buffer_size
buffer_copy
dynamic_vector_buffer
dynamic_string_buffer
transfer_all
transfer_at_least
transfer_exactly
<experimental/socket>
synopsissocket_base
socket_base::linger
basic_socket
basic_datagram_socket
basic_stream_socket
basic_socket_acceptor
<experimental/internet>
synopsisip::address
ip::address_v4
ip::address_v6
ip::bad_address_cast
ip::address_cast
ip::basic_address_iterator
specializationsip::basic_address_range
specializationsip::network_v4
ip::network_v6
ip::basic_endpoint
ip::basic_resolver_entry
ip::basic_resolver_results
ip::resolver_base
ip::basic_resolver
ip::tcp
ip::tcp::no_delay
ip::udp
ip::v6_only
ip::unicast::hops
ip::multicast::outbound_interface
ip::multicast::hops
ip::multicast::enable_loopback
This Technical Specification describes extensions to the C++ Standard Library. This Technical Specification specifies requirements for implementations of an interface that computer programs written in the C++ programming language may use to perform operations related to networking, such as operations involving sockets, timers, buffer management, host name resolution and internet protocols. This Technical Specification is applicable to information technology systems that can perform network operations, such as those with operating systems that conform to the POSIX interface. This Technical Specification is applicable only to vendors who wish to provide the interface it describes.
Conformance is specified in terms of behavior. Ideal behavior is not always implementable, so the conformance sub-clauses take that into account.
Some behavior is specified by reference to POSIX. How such behavior is actually implemented is unspecified.
[Note: This constitutes an "as if" rule allowing implementations to call native operating system or other APIs. —end note]
Implementations are encouraged to provide such behavior as it is defined by POSIX. Implementations shall document any behavior that differs from the behavior defined by POSIX. Implementations that do not support exact POSIX behavior are encouraged to provide behavior as close to POSIX behavior as is reasonable given the limitations of actual operating systems and file systems. If an implementation cannot provide any reasonable behavior, the implementation shall report an error as specified in Error Reporting.
[Note: This allows users to rely on an exception being thrown or an error code being set when an implementation cannot provide any reasonable behavior. —end note]
Implementations are not required to provide behavior that is not supported by a particular operating system.
This Technical Specification defines conditially-supported features, in
the form of additional member functions on types that satisfy Protocol
, Endpoint
, SettableSocketOption
, GettableSocketOption
or IoControlCommand
requirements.
[Note: This is so that, when the additional member functions are available, C++ programs may extend the library to add support for other protocols and socket options. —end note]
For the purposes of this Technical Specification, implementations that provide all of the additional member functions are known as extensible implementations.
[Note: Implementations are encouraged to provide the additional member functions, where possible. It is intended that POSIX and Windows implementations will provide them. —end note]
The following referenced documents are indispensable for the application of this document. For dated references, only the edition cited applies. For undated references, the latest edition of the referenced document (including any amendments) applies.
[Note: The programming language and library described
in ISO/IEC 14882 is herein called the C++ Standard. References to clauses
within the C++ Standard are written as "C++Std [xref]".
The operating system interface described in ISO/IEC 9945 is herein called
POSIX. —end note]
This Technical Specification mentions commercially available operating systems for purposes of exposition. [1]
Unless otherwise specified, the whole of the C++ Standard's Library introduction (C++Std [library]) is included into this Technical Specification by reference.
The components described in this Technical Specification are experimental
and not part of the C++ standard library. All components described in this
Technical Specification are declared in namespace std::experimental::network_v1
or a sub-namespace thereof unless
otherwise specified. The header described in this technical specification
shall import the contents of std::experimental::network_v1
into std::experimental
as if by:
namespace std { namespace experimental { inline namespace network_v1 {} } }
Unless otherwise specified, references to other entities described in this
Technical Specification are assumed to be qualified with std::experimental::network_v1::
,
references to entities described in the C++ standard are assumed to be qualified
with std::
,
and references to entities described in C++ Extensions for Library Fundamentals
are assumed to be qualified with std::experimental::fundamentals_v1::
.
[defs.host.byte.order] See section 3.194 of POSIX Base Definitions, Host Byte Order.
[defs.net.byte.order] See section 3.238 of POSIX Base Definitions, Network Byte Order.
[defs.sync.op] A synchronous operation is one where control is not returned until the operation completes.
[defs.async.op] An asynchronous operation is one where control is returned immediately without waiting for the operation to complete. Multiple asynchronous operations may be executed concurrently.
This section describes tentative plans for future versions of this technical specification and plans for moving content into future versions of the C++ Standard.
The C++ committee may release new versions of this technical specification,
containing networking library extensions we hope to add to a near-future
version of the C++ Standard. Future versions will define their contents in
std::experimental::network_v2
, std::experimental::network_v3
,
etc., with the most recent implemented version inlined into std::experimental
.
When an extension defined in this or a future version of this technical specification
represents enough existing practice, it will be moved into the next version
of the C++ Standard by removing the experimental::network_vN
segment of its namespace and by removing the experimental/
prefix from its header's path.
These macros allow users to determine which version of this Technical Specification is supported by the headers defined by the specification. All headers in this Technical Specification shall supply the following macro definition:
#define __cpp_lib_experimental_network yyyymm
If an implementation supplies all of the conditionally-supported features specified in [conformance.conditional], all headers in this Technical Specification shall supply the following macro definition:
#define __cpp_lib_experimental_network_extensible yyyymm
[Note: The value of the macros __cpp_lib_experimental_network
and __cpp_lib_experimental_network_extensible
is yyyymm
where yyyy
is the year and mm
the month when the version of the Technical Specification was completed.
—end note]
This subclause describes the conventions used to specify this Technical Specification, in addition to those conventions specified in C++ Std, [description].
In addition to the elements defined in C++Std [structure.specifications], descriptions of function semantics contain the following elements (as appropriate):
— Completion signature: - if the function initiates an asynchronous operation, specifies the signature of a completion handler used to receive the result of the operation.
Most synchronous network library functions provide two overloads, one that
throws an exception to report system errors, and another that sets an
error_code
(C++ Std, [syserr]).
[Note: This supports two common use cases:
— Uses where system errors are truly exceptional and indicate a serious
failure. Throwing an exception is the most appropriate response.
— Uses where system errors are routine and do not necessarily represent
failure. Returning an error code is the most appropriate response. This
allows application specific error handling, including simply ignoring the
error.
—end note]
Functions not having an argument of type
error_code&
report errors as follows, unless otherwise specified:
— When a call by the implementation to an operating system or other underlying
API results in an error that prevents the function from meeting its specifications,
the function exits via an exception of a type that would match a handler
of type system_error
.
— Destructors throw nothing.
Functions having an argument of type error_code&
report errors as follows, unless otherwise
specified:
— If a call by the implementation to an operating system or other underlying
API results in an error that prevents the function from meeting its specifications,
the error_code&
argument ec
is set as appropriate
for the specific error. Otherwise, the ec
argument is set such that !ec
is true
.
Where a function is specified as two overloads, with and without an argument
of type error_code&
:
R f(A1 a1, A2 a2, ..., AN aN); R f(A1 a1, A2 a2, ..., AN aN, error_code& ec);
then, when R is non-void
, the effects of the first overload
are as if:
error_code ec; R r(f(a1, a2, ..., aN, ec)); if (ec) throw system_error(ec, __func__); return r;
otherwise, when R
is void
, the effects of the
first overload are as if:
error_code ec; f(a1, a2, ..., aN, ec); if (ec) throw system_error(ec, __func__);
except that the type thrown may differ as specified above.
For both overloads, failure to allocate storage is reported by throwing an exception as described in the C++ standard (C++14 [res.on.exception.handling]).
Asynchronous network library functions in this Technical Specification
are identified by having the prefix async_
and take a completion handler [async.reqmts.async.token].
These asynchronous operations report errors as follows:
— If a call by the implementation to an operating system or other underlying
API results in an error that prevents the asynchronous operation from meeting
its specifications, the completion handler is invoked with an error_code
value ec
that is set as appropriate for the specific error. Otherwise, the error_code
value ec
is set such that !ec
is true
.
— Asynchronous operations shall not fail with an error condition that indicates
interruption of an operating system or underlying API by a signal [Note:
Such as POSIX error number EINTR
—end note] . Asynchronous operations shall not fail
with any error condition associated with non-blocking operations [Note:
Such as POSIX error numbers EWOULDBLOCK
,
EAGAIN
, or EINPROGRESS
; Windows error numbers WSAEWOULDBLOCK
or WSAEINPROGRESS
—end note] .
Unless otherwise specified, when the behavior of a synchronous or asynchronous
operation is defined "as if" implemented by a POSIX function,
the error_code
produced
by the function shall meet the following requirements:
— If the failure condition is one that is listed by POSIX for that function,
the error_code
shall compare
equal to the error's corresponding enum
class errc
(C++Std [syserr]) or enum class resolver_errc
constant.
— Otherwise, the error_code
shall be set to an implementation-defined value that reflects the underlying
operating system error.
[Example: The POSIX specification for shutdown
lists EBADF
as one of its
possible errors. If a function that is specified "as if" implemented
by shutdown
fails with EBADF
then the
following condition holds for the error_code
value ec
: ec == errc::bad_file_descriptor
—end example]
When the description of a function contains the element Error
conditions, this lists conditions where the operation may fail.
The conditions are listed, together with a suitable explanation, as enum class
constants. Unless otherwise specified, this list is a subset of the failure
conditions associated with the function.
Table 1. Networking library summary
Clause |
Header(s) |
---|---|
| |
| |
| |
| |
| |
| |
| |
|
Throughout this Technical Specification, the names of the template parameters are used to express type requirements, as listed in the table below.
Table 2. Template parameters and type requirements
template parameter name |
type requirements |
---|---|
| |
|
C++Std, [allocator.requirements] |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
#include <experimental/executor> #include <experimental/io_service> #include <experimental/timer> #include <experimental/buffer> #include <experimental/socket> #include <experimental/internet>
[Note: This header is provided as a convenience for
programs so that they may access all networking facilities via a single,
self-contained #include
.
—end note]
namespace std { namespace experimental { inline namespace network_v1 { class execution_context; template<class T, class Executor> class executor_wrapper; template<class Executor> class executor_work; class system_executor; class executor; template<class Executor> class strand; class io_service; template<class Clock> struct wait_traits; template<class Clock, class WaitTraits = wait_traits<Clock>> class basic_waitable_timer; typedef basic_waitable_timer<chrono::system_clock> system_timer; typedef basic_waitable_timer<chrono::steady_clock> steady_timer; typedef basic_waitable_timer<chrono::high_resolution_clock> high_resolution_timer; template<class Protocol> class basic_socket; template<class Protocol> class basic_datagram_socket; template<class Protocol> class basic_stream_socket; template<class Protocol> class basic_socket_acceptor; template<class Protocol, class Clock = chrono::steady_clock, class WaitTraits = wait_traits<Clock>> class basic_socket_streambuf; template<class Protocol, class Clock = chrono::steady_clock, class WaitTraits = wait_traits<Clock>> class basic_socket_iostream; namespace ip { class address; class address_v4; class address_v6; class address_iterator_v4; class address_iterator_v6; class address_range_v4; class address_range_v6; class network_v4; class network_v6; template<class InternetProtocol> class basic_endpoint; template<class InternetProtocol> class basic_resolver_entry; template<class InternetProtocol> class basic_resolver_results; template<class InternetProtocol> class basic_resolver; class tcp; class udp; } // namespace ip } // inline namespace network_v1 } // namespace experimental } // namespace std
Default template arguments are described as appearing both in <netfwd>
and in the synopsis of other headers
but it is well-formed to include both <netfwd>
and one or more of the other headers. [Note: It is
the implementation’s responsibility to implement headers so that including
<netfwd>
and other headers does not violate
the rules about multiple occurrences of default arguments. —end
note]
namespace std { namespace experimental { inline namespace network_v1 { template<class CompletionToken, class Signature, class = void> struct completion_handler_type; template<class CompletionToken, class Signature> using completion_handler_type_t = typename completion_handler_type<CompletionToken, Signature>::type; template<class CompletionHandler> class async_result; template<class CompletionToken, class Signature> struct async_completion; template<class T, class ProtoAllocator = allocator<void>> struct associated_allocator; template<class T, class ProtoAllocator = allocator<void>> using associated_allocator_t = typename associated_allocator<T, ProtoAllocator>::type; // get_associated_allocator: template<class T> associated_allocator_t<T> get_associated_allocator(const T& t); template<class T, class ProtoAllocator> associated_allocator_t<T, ProtoAllocator> get_associated_allocator(const T& t, const ProtoAllocator& a); enum class fork_event { prepare, parent, child }; class execution_context; class service_already_exists; template<class Service> Service& use_service(execution_context& ctx); template<class Service, class... Args> Service& make_service(execution_context& ctx, Args&&... args); template<class Service> bool has_service(execution_context& ctx) noexcept; template<class T> struct is_executor : false_type {}; struct executor_arg_t { }; constexpr executor_arg_t executor_arg = executor_arg_t(); template<class T, class Executor> struct uses_executor; template<class T, class Executor = system_executor> struct associated_executor; template<class T, class Executor = system_executor> using associated_executor_t = typename associated_executor<T, Executor>::type; // get_associated_executor: template<class T> associated_executor_t<T> get_associated_executor(const T& t); template<class T, class Executor> associated_executor_t<T, Executor> get_associated_executor(const T& t, const Executor& ex); template<class T, class ExecutionContext> associated_executor_t<T, typename ExecutionContext::executor_type> get_associated_executor(const T& t, ExecutionContext& ctx); template<class T, class Executor> class executor_wrapper; template<class T, class Executor, class Signature> struct completion_handler_type<executor_wrapper<T, Executor>, Signature>; template<class T, class Executor> class async_result<executor_wrapper<T, Executor>>; template<class T, class Executor, class ProtoAllocator> struct associated_allocator<executor_wrapper<T, Executor>, ProtoAllocator>; template<class T, class Executor, class Executor1> struct associated_executor<executor_wrapper<T, Executor>, Executor1>; // wrap: template<class Executor, class T> executor_wrapper<decay_t<T>, Executor> wrap(const Executor& ex, T&& t); template<class ExecutionContext, class T> executor_wrapper<decay_t<T>, typename ExecutionContext::executor_type> wrap(ExecutionContext& ctx, T&& t); template<class Executor> class executor_work; // make_work: template<class Executor> executor_work<Executor> make_work(const Executor& ex); template<class ExecutionContext> executor_work<typename ExecutionContext::executor_type> make_work(ExecutionContext& ctx); template<class T> executor_work<associated_executor_t<T>> make_work(const T& t); template<class T, class Executor> executor_work<associated_executor_t<T, Executor>> make_work(const T& t, const Executor& ex); template<class T, class ExecutionContext> executor_work<associated_executor_t<T, typename ExecutionContext::executor_type>> make_work(const T& t, ExecutionContext& ctx); class system_executor; bool operator==(const system_executor&, const system_executor&); bool operator!=(const system_executor&, const system_executor&); template<> struct is_executor<system_executor> : true_type {}; class bad_executor; class executor; template <> struct is_executor<executor> : true_type {}; bool operator==(const executor& a, const executor& b) noexcept; bool operator==(const executor& e, nullptr_t) noexcept; bool operator==(nullptr_t, const executor& e) noexcept; bool operator!=(const executor& a, const executor& b) noexcept; bool operator!=(const executor& e, nullptr_t) noexcept; bool operator!=(nullptr_t, const executor& e) noexcept; // dispatch: template<class CompletionToken> DEDUCED dispatch(CompletionToken&& token); template<class Executor, class CompletionToken> DEDUCED dispatch(const Executor& ex, CompletionToken&& token); template<class ExecutionContext, class CompletionToken> DEDUCED dispatch(ExecutionContext& ctx, CompletionToken&& token); // post: template<class CompletionToken> DEDUCED post(CompletionToken&& token); template<class Executor, class CompletionToken> DEDUCED post(const Executor& ex, CompletionToken&& token); template<class ExecutionContext, class CompletionToken> DEDUCED post(ExecutionContext& ctx, CompletionToken&& token); // defer: template<class CompletionToken> DEDUCED defer(CompletionToken&& token); template<class Executor, class CompletionToken> DEDUCED defer(const Executor& ex, CompletionToken&& token); template<class ExecutionContext, class CompletionToken> DEDUCED defer(ExecutionContext& ctx, CompletionToken&& token); template<class Executor> class strand; template<class Executor> bool operator==(const strand<Executor>& a, const strand<Executor>& b); template<class Executor> bool operator!=(const strand<Executor>& a, const strand<Executor>& b); template<class Executor> struct is_executor<strand<Executor>> : true_type {}; template<class ProtoAllocator = allocator<void>> class use_future_t; constexpr use_future_t<> use_future = use_future_t<>(); template<class ProtoAllocator, class R, class... Args> struct completion_handler_type<use_future_t<ProtoAllocator>, R(Args...)>; template<class R, class... Args> class async_result<packaged_task<R(Args...)>>; template<class, class> class packaged_handler; // undefined template<class R, class... Arg, class ProtoAllocator> class packaged_handler<R(Args...), ProtoAllocator>; template<class Signature, class ProtoAllocator> class async_result<packaged_handler<Signature, ProtoAllocator>>; template<class Func, class ProtoAllocator = allocator<void>> class packaged_token; template<class Func, class ProtoAllocator, class R, class... Args> struct completion_handler_type<packaged_token<Func, ProtoAllocator>, R(Args...)>; template<class Func, class ProtoAllocator = allocator<void>> packaged_token<decay_t<Func>, ProtoAllocator> package( Func&& f, const ProtoAllocator& a = ProtoAllocator()); } // inline namespace network_v1 } // namespace experimental template<class Allocator> struct uses_allocator<experimental::network_v1::executor, Allocator> : true_type {}; } // namespace std
[async.reqmts.proto.allocator]
A type A
meets the proto-allocator
requirements if A
is
CopyConstructible
(C++Std,
[copyconstructible]), Destructible
(C++Std, [destructible]), and allocator_traits<A>::rebind_alloc<U>
meets the allocator requirements
(C++Std, [allocator.requirements]), where U
is an object type. [Note: For example, std::allocator<void>
meets the proto-allocator requirements but not the allocator requirements.
—end note] No constructor, comparison operator, copy
operation, move operation, or swap operation on these types shall exit
via an exception.
In this Technical Specification, an asynchronous operation is initiated
by a function that is named with the prefix async_
.
These functions shall be known as initiating functions.
All initiating functions in this Technical Specification:
— are function templates with template parameter CompletionToken
;
— accept, as the final parameter, a completion token
object token
of type
CompletionToken
;
— specify a Completion signature element that defines
a call signature Signature
(C++ Std [func.def]).
A completion handler is a function object that will be invoked, at most once, with the result of an asynchronous operation.
An initiating function determines the type CompletionHandler
of its completion handler function object by performing completion_handler_type_t<CompletionToken,
Signature>
.
The completion handler object completion_handler
is initialized with completion_handler(forward<CompletionToken>(token))
. [Note: No other
requirements are placed on the type CompletionToken
.
—end note]
The type CompletionHandler
must satisfy the MoveConstructible
requirements (C++ Std, [moveconstructible]) and be callable with the
specified completion signature. The Completion signature
elements in this Technical Specification have named parameters, and
the result of an asynchronous operation is specified in terms of these
names.
[async.reqmts.async.return.type]
For the sake of exposition, this specification sometimes annotates
functions with a return type DEDUCED
. For every function declaration
that returns DEDUCED
, the meaning is equivalent to specifying
the return type as typename
async_result<CompletionHandler>::type
.
[async.reqmts.async.return.value]
An initiating function in this Technical Specification produces its return type as follows:
— constructing an object result
of type async_result<CompletionHandler>
, initialized as result(handler)
; and
— using result.get()
as the operand of the return statement.
[Example: Given an asynchronous operation with
Completion signature void(R1 r1, R2 r2)
, an initiating function meeting these
requirements may be implemented as follows:
template<class CompletionToken> auto async_xyz(T1 t1, T2 t2, CompletionToken&& token) { completion_handler_type_t<decay_t<CompletionToken>, void(R1 r1, R2 r2)> completion_handler(forward<CompletionToken>(token)); async_result<decltype(completion_handler)> result(completion_handler); // initiate the operation and cause completion_handler to be invoked with // the result return result.get(); }
For convenience, initiating functions may be implemented using the
async_completion
template:
template<class CompletionToken> auto async_xyz(T1 t1, T2 t2, CompletionToken&& token) { async_completion<CompletionToken, void(R1 r1, R2 r2)> init(token); // initiate the operation and cause init.completion_handler to be invoked // with the result return init.result.get(); }
—end example]
Unless otherwise specified, the lifetime of arguments to initiating functions shall be treated as follows:
— If the parameter has a pointer type or has a type of lvalue reference to non-const, the implementation may assume the validity of the pointee or referent, respectively, until the completion handler is invoked. [Note: In other words, the program must guarantee the validity of the argument until the completion handler is invoked. —end note]
— Otherwise, the implementation must not assume the validity of the argument after the initiating function completes. [Note: In other words, the program is not required to guarantee the validity of the argument after the initiating function completes. —end note] The implementation may make copies of the argument, and all copies shall be destroyed no later than immediately after invocation of the completion handler.
[async.reqmts.async.assoc.exec]
Certain objects that participate in asynchronous operations have an associated executor. These are obtained as specified below.
All asynchronous operations in this Technical Specification have an
associated executor object satisfying the Executor
requirements. If the initiating function is a member function,
the associated executor is that returned by the get_executor
member function on the same object. If the initiating function is not
a member function, the associated executor is that returned by the
get_executor
member
function of the first argument to the initiating function.
Let Executor1
be the
type of the associated executor, and ex1
be the associated executor object obtained as described above.
[async.reqmts.async.handler.exec]
A completion handler object of type CompletionHandler
has an associated executor object ex2
of type Executor2
satisfying
the Executor requirements.
The type Executor2
is associated_executor_t<CompletionHandler, Executor1>
. The object ex2
is obtained by performing associated_executor<CompletionHandler, Executor1>::get(handler, ex1)
.
The implementation of an asynchronous operation shall maintain an object
work1
of type executor_work<Executor1>
,
initialized with work1(ex1)
and with work1.owns_work() == true
, until the effects of the asynchronous
operation have been realized.
The implementation of an asynchronous operation shall maintain an object
work2
of type executor_work<Executor2>
,
initialized with work2(ex2)
and with work2.owns_work() == true
, until handler
has been submitted for execution.
Asynchronous operations may allocate memory. [Note:
Such as a data structure to store copies of the handler
object and the initiating function's arguments. —end note]
Let Alloc1
be a type,
satisfying the ProtoAllocator
requirements,
that represents the asynchronous operation's default allocation strategy.
[Note: Typically std::allocator<void>
. —end note]
Let alloc1
be an object
of type Alloc1
.
All handlers have an associated allocator object satisfying the ProtoAllocator
requirements. The type Alloc2
of the handler's associated allocator shall be determined by associated_allocator_t<CompletionHandler,
Alloc1>
.
The handler's associated allocator object alloc2
shall be obtained by performing associated_allocator<CompletionHandler, Executor1>::get(handler, alloc1)
.
The asynchronous operations defined in this Technical Specification:
— If required, allocate memory using only the completion handler's associated allocator.
— Prior to completion handler execution, deallocates any memory allocated.
[Note: The implementation may perform operating system or underlying API calls that perform memory allocations not using the associated allocator. Invocations of the allocator functions may not introduce data races (See C++ Std, [res.on.data.races]). —end note]
[async.reqmts.async.completion]
When an asynchronous operation completes, the implementation constructs
a zero-argument function object f
to invoke handler
with
the results of the operation.
If an asynchonous operation completes immediately (that is, within
the thread of execution calling the initiating function, and before
the initiating function returns), the completion handler shall be submitted
for execution as if by performing ex2.post(std::move(f), alloc2)
. Otherwise, the completion handler
shall be submitted for execution as if by performing ex2.dispatch(std::move(f), alloc2)
.
[async.reqmts.async.exceptions]
Completion handlers are permitted to throw exceptions. The effect of any exception propagated from the execution of a completion handler is determined by the executor which is executing the handler.
The library describes a standard set of requirements for executors. A type meeting the Executor requirements embodies a set of rules for determining how submitted function objects are to be executed.
An executor type X
shall
satisfy the requirements of CopyConstructible
(C++ Std, [copyconstructible]) and Destructible
(C++ Std, [destructible]) types. No constructor, comparison operator,
copy operation, move operation, swap operation, or member functions
context
, on_work_started
, and on_work_finished
on these types shall
exit via an exception.
The executor copy constructor, comparison operators, and other member functions defined in these requirements shall not introduce data races as a result of concurrent calls to those functions from different threads.
In the table below, X
denotes an executor class, x1
and x2
denote values
of type X
, cx1
and cx2
denote (possibly const) values of type X
,
mx1
denotes an xvalue
of type X
, f
denotes a MoveConstructible
(C++ Std, [moveconstructible]) function object callable with zero arguments,
a
denotes a value of
type A
meeting the Allocator
requirements (C++ Std, [allocator.requirements]),
and u
denotes an identifier.
Table 3. Executor requirements
expression |
type |
assertion/note |
---|---|---|
|
Shall not exit via an exception. | |
|
Shall not exit via an exception. | |
|
|
Returns |
|
|
Same as |
|
|
Shall not exit via an exception. |
|
Shall not exit via an exception. | |
|
Shall not exit via an exception. | |
|
Effects: Creates an object | |
|
Effects: Creates an object | |
|
Effects: Creates an object |
Although the requirements placed on defer
are identical to post
,
defer
is used to convey
the intention of the caller that the submitted function is a continuation
of the current call context. The executor may use this information to
optimize or otherwise adjust the way in which f
is invoked.
[async.reqmts.executioncontext]
An execution context shall be publicly and unambiguously derived from
execution_context
, and
meet the additional requirements listed below.
In the table below, X
denotes an execution context class, and x
denotes an object of type X
.
Table 4. ExecutionContext requirements
expression |
return type |
assertion/note |
---|---|---|
|
type meeting executor requirements | |
|
|
Returns an executor object that is associated with the execution context. |
A class is a service if it is publicly and unambiguously
derived from execution_context::service
,
or if it is publicly and unambiguously derived from another service.
For a service S
, S::key_type
is valid and denotes a type
(C++Std, [temp.deduct]), and is_base_of_v<typename
S::key_type,
S>
is true
.
A service defines an explicit constructor that can be invoked with a
single lvalue argument referring to the execution_context
object that will own the service. Any additional constructors shall take
a first lvalue argument referring to the execution_context
object that will own the service. [Note: These constructors
may be called by the make_service
function. —end note]
[Example:
class my_service : public execution_context::service { public: typedef my_service key_type; explicit my_service(execution_context& ctx); my_service(execution_context& ctx, int some_value); private: virtual void shutdown() override; ... };
—end example]
A service's shutdown
member function shall destroy all copies of user-defined function objects
that are held by the service.
A type satisfies the signature requirements if it is a call signature (C++ Std, [func.def]).
The completion_handler_type
class template is a type transformation (C++Std, [meta]) that transforms
a CompletionToken
into
a completion handler type that is based on a Signature
.
namespace std { namespace experimental { inline namespace network_v1 { template<class CompletionToken, class Signature, class = void> struct completion_handler_type { typedef CompletionToken type; }; } // inline namespace network_v1 } // namespace experimental } // namespace std
The template parameter CompletionToken
shall be an object type. The template parameter Signature
shall be a call signature (C++Std, [func.def]).
Specializations of completion_handler_type
shall define a nested type type
that satisfies the MoveConstructible
requirements. An object of type type
shall be a function object with call signature Signature
,
and type
shall be constructible
from a rvalue of type CompletionToken
.
The template parameter CompletionHandler
is a completion handler type produced by completion_handler_type_t<T, S>
for some completion token type T
and call signature S
.
namespace std { namespace experimental { inline namespace network_v1 { template<class CompletionHandler> class async_result { public: typedef void type; explicit async_result(CompletionHandler&) {} async_result(const async_result&) = delete; async_result& operator=(const async_result&) = delete; type get() {} }; } // inline namespace network_v1 } // namespace experimental } // namespace std
Specializations of async_result
shall satisfy the Destructible
requirements (C++Std, [destructible]) in addition to the requirements in
the table below. In this table, R
is a specialization of async_result
for the template parameter CompletionHandler
;
r
is a modifiable lvalue
of type R
; and h
is a modifiable lvalue of type CompletionHandler
.
Table 5. async_result specialization requirements
Expression |
Return type |
Note |
---|---|---|
|
| |
| ||
|
|
The |
Template parameter CompletionToken
specifies the model used to obtain the result of the asynchronous operation.
Template parameter Signature
is the call signature (C++ Std, [func.def]) for the completion handler
type invoked on completion of the asynchronous operation.
namespace std { namespace experimental { inline namespace network_v1 { template<class CompletionToken, class Signature> struct async_completion { typedef completion_handler_type_t<decay_t<CompletionToken>, Signature> completion_handler_type; explicit async_completion(CompletionToken& t); async_completion(const async_completion&) = delete; async_completion& operator=(const async_completion&) = delete; see below completion_handler; async_result<completion_handler_type> result; }; } // inline namespace network_v1 } // namespace experimental } // namespace std
The template parameter Signature
shall be a call signature (C++Std, [func.def]).
[async.async.completion.members]
explicit async_completion(CompletionToken& t);
Effects: If
CompletionToken
andcompletion_handler_type
are the same type, bindscompletion_handler
tot
; otherwise, initializescompletion_handler
with the result offorward<CompletionToken>(t)
. Initializesresult
withcompletion_handler
.
see below completion_handler;
Type:
completion_handler_type&
ifCompletionToken
andcompletion_handler_type
are the same type; otherwise,completion_handler_type
.
namespace std { namespace experimental { inline namespace network_v1 { template<class T, class ProtoAllocator = allocator<void>> struct associated_allocator { typedef see below type; static type get(const T& t, const ProtoAllocator& a = ProtoAllocator()) noexcept; }; } // inline namespace network_v1 } // namespace experimental } // namespace std
Specializations of associated_allocator
shall satisfy the requirements in the table below. In this table, X
is a specialization of associated_allocator
for the template
parameters T
and ProtoAllocator
; t
is a value of (possibly const) T
;
and a
is an object of type
ProtoAllocator
.
Table 6. associated_allocator specialization requirements
Expression |
Return type |
Note |
---|---|---|
|
A type meeting the proto-allocator requirements. | |
|
|
Shall not exit via an exception. |
|
|
Shall not exit via an exception. |
typedef see below type;
Type: If
T
has a nested typeallocator_type
,typename T::allocator_type
. OtherwiseProtoAllocator
.
type get(const T& t, const ProtoAllocator& a = ProtoAllocator()) noexcept;
Returns: If
T
has a nested typeallocator_type
,t.get_allocator()
. Otherwisea
.
template<class T> associated_allocator_t<T> get_associated_allocator(const T& t);
Returns:
associated_allocator<T>::get(t)
.
template<class T, class ProtoAllocator> associated_allocator_t<T, ProtoAllocator> get_associated_allocator(const T& t, const ProtoAllocator& a);
Returns:
associated_allocator<T, ProtoAllocator>::get(t, a)
.
Class execution_context
implements an extensible, type-safe, polymorphic set of services, indexed
by service type.
namespace std { namespace experimental { inline namespace network_v1 { class execution_context { public: class service; // construct / copy / destroy: execution_context(); execution_context(const execution_context&) = delete; execution_context& operator=(const execution_context&) = delete; virtual ~execution_context(); // execution context operations: void notify_fork(fork_event e); protected: // execution context protected operations: void shutdown(); void destroy(); }; // service access: template<class Service> typename Service::key_type& use_service(execution_context& ctx); template<class Service, class... Args> Service& make_service(execution_context& ctx, Args&&... args); template<class Service> bool has_service(const execution_context& ctx) noexcept; class service_already_exists : public logic_error { }; } // inline namespace network_v1 } // namespace experimental } // namespace std
Access to the services of an execution_context
is via three function templates, use_service<>
, make_service<>
and has_service<>
.
In a call to use_service<Service>()
, the type argument chooses a service.
If the service is not present in an execution_context
,
an object of type Service
is created and added to the execution_context
.
A program can check if an execution_context
implements a particular service with the function template has_service<Service>()
.
Service objects may be explicitly added to an execution_context
using the function template make_service<Service>()
. If the service is already present,
make_service
exits via
an exception of type service_already_exists
.
Once a service reference is obtained from an execution_context
object by calling use_service<>
, that reference remains usable
until a call to destroy()
.
execution_context();
Effects: Creates an object of class
execution_context
which contains no services. [Note: An implementation might preload service of internal service types for its own use. —end note]
~execution_context();
Effects: Destroys an object of class
execution_context
. Performsshutdown()
followed bydestroy()
.
void notify_fork(fork_event e);
Effects: For each service object
svc
in the set:
— Ife == fork_event::prepare
, performssvc->notify_fork(e)
in reverse order of addition to the set.
— Otherwise, performssvc->notify_fork(e)
in order of addition to the set.
void shutdown();
Effects: For each service object
svc
in theexecution_context
set, in reverse order of addition to the set, performssvc->shutdown()
. For each service in the set,svc->shutdown()
is called only once irrespective of the number of calls toshutdown
on theexecution_context
.
void destroy();
[function_effects Destroys each service object in the execution_context
set, and removes it from the set, in reverse order of addition to the
set.
The functions use_service
,
make_service
, and has_service
shall not introduce data
races as a result of concurrent calls to those functions from different
threads.
template<class Service> typename Service::key_type& use_service(execution_context& ctx);
Effects: If an object of type
Service::key_type
does not already exist in theexecution_context
set identified byctx
, creates an object of typeService
, initialized asService(ctx)
, and adds it to the set.
Returns: A reference to the corresponding service of
ctx
.
Notes: The reference returned remains valid until a call to
destroy
.
template<class Service, class... Args> Service& make_service(execution_context& ctx, Args&&... args);
Requires: A service object of type
Service::key_type
does not already exist in theexecution_context
set identified byctx
.
Effects: Creates an object of type
Service
, initialized asService(ctx, forward<Args>(args)...)
, and adds it to theexecution_context
set identified byctx
.
Throws:
service_already_exists
if a corresponding service object of typeKey
is already present in the set.
Notes: The reference returned remains valid until a call to
destroy
.
template<class Service> bool has_service(const execution_context& ctx) noexcept;
Returns:
true
if an object of typeService::key_type
is present inctx
, otherwisefalse
.
namespace std { namespace experimental { inline namespace network_v1 { class execution_context::service { protected: // construct / copy / destroy: explicit service(execution_context& owner); service(const service&) = delete; service& operator=(const service&) = delete; virtual ~service(); // service observers: execution_context& context() noexcept; private: // service operations: virtual void shutdown() = 0; virtual void notify_fork(fork_event e) {} execution_context& context_; // exposition only }; } // inline namespace network_v1 } // namespace experimental } // namespace std
explicit service(execution_context& owner);
Postconditions:
std::addressof(context_) == std::addressof(owner)
.
execution_context& context() noexcept;
Returns:
context_
.
namespace std { namespace experimental { inline namespace network_v1 { template<class T> struct is_executor : false_type {}; } // inline namespace network_v1 } // namespace experimental } // namespace std
is_executor
can be used
to detect executor types satisfying the Executor
type requirements.
Instantiations of the is_executor
template shall meet the UnaryTypeTrait requirements (C++ Std, [meta.rqmts]).
A program may specialize this template for a user-defined type T
to have a BaseCharacteristic of integral_constant<int,
N
>
with N
> 0
to indicate that T
should be treated as an executor type.
namespace std { namespace experimental { inline namespace network_v1 { struct executor_arg_t { }; constexpr executor_arg_t executor_arg = executor_arg_t(); } // inline namespace network_v1 } // namespace experimental } // namespace std
The executor_arg_t
struct
is an empty structure type used as a unique type to disambiguate constructor
and function overloading. Specifically, types may have constructors with
executor_arg_t
as the first
argument, immediately followed by an argument of a type that satisfies
the Executor requirements.
namespace std { namespace experimental { inline namespace network_v1 { template<class T, class Executor> struct uses_executor; } // inline namespace network_v1 } // namespace experimental } // namespace std
Remark: Detects whether T
has a nested executor_type
that is convertible from Executor
.
Meets the BinaryTypeTrait
requirements (C++ Std, [meta.rqmts]). The implementation shall provide
a definition that is derived from true_type
if a type T::executor_type
exists and is_convertible<Executor,
T::executor_type>::value !=
false
, otherwise it shall be derived
from false_type
. A program
may specialize this template to derive from true_type
for a user-defined type T
that does not have a nested executor_type
but nonetheless can be constructed with an executor if the first argument
of a constructor has type executor_arg_t
and the second argument has type Executor
.
Uses-executor construction with executor Executor
refers to the construction
of an object obj
of type
T
, using constructor
arguments v1,
v2,
..., vN
of types V1,
V2,
..., VN
,
respectively, and an executor ex
of type Executor
, according
to the following rules:
— if uses_executor<T, Executor>::value
is true
and is_constructible<T, executor_arg_t, Executor, V1, V2, ..., VN>::value
is true
,
then obj
is initialized
as obj(executor_arg,
ex,
v1,
v2,
..., vN)
;
— otherwise, obj
is initialized
as obj(v1, v2, ..., vN)
.
namespace std { namespace experimental { inline namespace network_v1 { template<class T, class Executor = system_executor> struct associated_executor { typedef see below type; static type get(const T& t, const Executor& e = Executor()) noexcept; }; } // inline namespace network_v1 } // namespace experimental } // namespace std
A program may specialize this traits type if the T
template parameter in the specialization is a user-defined type. The template
parameter Executor
shall
be a type meeting Executor requirements.
Specializations of associated_executor
shall satisfy the requirements in the table below. In this table, X
is a specialization of associated_executor
for the template
parameter T
; t
is a const reference to an object of
type T
; and e
is an object of type Executor
.
Table 7. associated_executor specialization requirements
Expression |
Return type |
Note |
---|---|---|
|
A type meeting Executor requirements. | |
|
|
Shall not exit via an exception. |
|
|
Shall not exit via an exception. |
typedef see below type;
Type: If
T
has a nested typeexecutor_type
,typename T::executor_type
. OtherwiseExecutor
.
type get(const T& t, const Executor& e = Executor()) noexcept;
Returns: If
T
has a nested typeexecutor_type
,t.get_executor()
. Otherwisee
.
template<class T> associated_executor_t<T> get_associated_executor(const T& t);
Returns:
associated_executor<T>::get(t)
.
template<class T, class Executor> associated_executor_t<T, Executor> get_associated_executor(const T& t, const Executor& ex);
Returns:
associated_executor<T, Executor>::get(t, ex)
.
Remarks: This function shall not participate in overload resolution unless
is_executor<Executor>::value
istrue
.
template<class T, class ExecutionContext> associated_executor_t<T, typename ExecutionContext::executor_type> get_associated_executor(const T& t, ExecutionContext& ctx);
Returns:
get_associated_executor(t, ctx.get_executor())
.
Remarks: This function shall not participate in overload resolution unless
is_convertible<ExecutionContext&, execution_context&>::value
istrue
.
executor_wrapper<T, Executor>
is a wrapper around an object or function
of type T
, and an executor
object of type Executor
satisfying Executor requirements.
namespace std { namespace experimental { inline namespace network_v1 { template<class T, class Executor> class executor_wrapper { public: // types: typedef T wrapped_type; typedef Executor executor_type; // construct / copy / destroy: executor_wrapper(T t, const Executor& ex); executor_wrapper(const executor_wrapper& other) = default; executor_wrapper(executor_wrapper&& other) = default; template<class U, class OtherExecutor> executor_wrapper(const executor_wrapper<U, OtherExecutor>& other); template<class U, class OtherExecutor> executor_wrapper(executor_wrapper<U, OtherExecutor>&& other); template<class U, class OtherExecutor> executor_wrapper(executor_arg_t, const Executor& ex, const executor_wrapper<U, OtherExecutor>& other); template<class U, class OtherExecutor> executor_wrapper(executor_arg_t, const Executor& ex, executor_wrapper<U, OtherExecutor>&& other); ~executor_wrapper(); // executor wrapper access: T& unwrap() noexcept; const T& unwrap() const noexcept; executor_type get_executor() const noexcept; // executor wrapper invocation: template<class... Args> result_of_t<T&(Args&&...)> operator()(Args&&... args); template<class... Args> result_of_t<const T&(Args&&...)> operator()(Args&&... args) const; private: Executor ex_; // exposition only T wrapped_; // exposition only }; template<class T, class Executor, class Signature> struct completion_handler_type<executor_wrapper<T, Executor>, Signature> { typedef executor_wrapper<completion_handler_type_t<T, Signature>, Executor> type; }; template<class T, class Executor> class async_result<executor_wrapper<T, Executor>>; template<class T, class Executor, class ProtoAllocator> struct associated_allocator<executor_wrapper<T, Executor>, ProtoAllocator>; template<class T, class Executor, class Executor1> struct associated_executor<executor_wrapper<T, Executor>, Executor1>; } // inline namespace network_v1 } // namespace experimental } // namespace std
executor_wrapper(T t, const Executor& ex);
Effects: Constructs an object of type
executor_wrapper<T, Executor>
. Initializesex_
with the valueex
. Initializeswrapped_
by performing uses-executor construction, using the constructor argumentstd::move(t)
and the executorex_
.
template<class U, class OtherExecutor> executor_wrapper(const executor_wrapper<U, OtherExecutor>& other);
Requires: If
U
is not convertible toT
, or ifOtherExecutor
is not convertible toExecutor
, the program is ill-formed.
Effects: Constructs an object of type
executor_wrapper<T, Executor>
. Initializesex_
withother.get_executor()
. Initializeswrapped_
by performing uses-executor construction, using the constructor argumentother.unwrap()
and the executorex_
.
template<class U, class OtherExecutor> executor_wrapper(executor_wrapper<U, OtherExecutor>&& other);
Requires: If
U
is not convertible toT
, or ifOtherExecutor
is not convertible toExecutor
, the program is ill-formed.
Effects: Constructs an object of type
executor_wrapper<T, Executor>
. Initializesex_
withother.get_executor()
. Initializeswrapped_
by performing uses-executor construction, using the constructor argumentstd::move(other.unwrap())
and the executorex_
.
template<class U, class OtherExecutor> executor_wrapper(executor_arg_t, const Executor& ex, const executor_wrapper<U, OtherExecutor>& other);
Requires: If
U
is not convertible toT
the program is ill-formed.
Effects: Constructs an object of type
executor_wrapper<T, Executor>
. Initializesex_
withex
. Initializeswrapped_
by performing uses-executor construction, using the constructor argumentother.unwrap()
and the executorex_
.
template<class U, class OtherExecutor> executor_wrapper(executor_arg_t, const Executor& ex, executor_wrapper<U, OtherExecutor>&& other);
Requires:
U
isT
or convertible toT
.
Effects: Constructs an object of type
executor_wrapper<T, Executor>
. Initializesex_
withex
. Initializeswrapped_
by performing uses-executor construction, using the constructor argumentstd::move(other.unwrap())
and the executorex_
.
T& unwrap() noexcept; const T& unwrap() const noexcept;
Returns:
wrapped_
.
executor_type get_executor() const noexcept;
Returns:
executor_
.
[async.exec.wrapper.invocation]
template<class... Args> result_of_t<T&(Args&&...)> operator()(Args&&... args); template<class... Args> result_of_t<const T&(Args&&...)> operator()(Args&&... args) const;
Returns:
INVOKE
(unwrap(), forward<Args>(args)...)
(C++ Std, [func.require]).
[async.exec.wrapper.async.result]
namespace std { namespace experimental { inline namespace network_v1 { template<class T, class Executor> class async_result<executor_wrapper<T, Executor>> { public: typedef typename async_result<T>::type type; explicit async_result(executor_wrapper<T, Executor>& wrapper); async_result(const async_result&) = delete; async_result& operator=(const async_result&) = delete; type get(); private: async_result<T> wrapped_; // exposition only }; } // inline namespace network_v1 } // namespace experimental } // namespace std explicit async_result(executor_wrapper<T, Executor>& wrapper);
Effects: Initializes
wrapped_
aswrapped_(wrapper.unwrap())
.
type get();
Returns:
wrapped_.get()
.
[async.exec.wrapper.assoc.alloc]
namespace std { namespace experimental { inline namespace network_v1 { template<class T, class Executor, class ProtoAllocator> struct associated_allocator<executor_wrapper<T, Executor>, ProtoAllocator> { typedef associated_allocator_t<T, ProtoAllocator> type; static type get(const executor_wrapper<T, Executor>& w, const ProtoAllocator& a = ProtoAllocator()) noexcept; }; } // inline namespace network_v1 } // namespace experimental } // namespace std static type get(const executor_wrapper<T, Executor>& w, const ProtoAllocator& a = ProtoAllocator()) noexcept;
Returns:
associated_allocator<T, ProtoAllocator>::get(w.unwrap(), a)
.
[async.exec.wrapper.assoc.exec]
namespace std { namespace experimental { inline namespace network_v1 { template<class T, class Executor, class Executor1> struct associated_executor<executor_wrapper<T, Executor>, Executor1> { typedef Executor type; static type get(const executor_wrapper<T, Executor>& w, const Executor1& e = Executor1()) noexcept; }; } // inline namespace network_v1 } // namespace experimental } // namespace std static type get(const executor_wrapper<T, Executor>& w, const Executor1& e = Executor1()) noexcept;
Returns:
w.get_executor()
.
template<class Executor, class T> executor_wrapper<decay_t<T>, Executor> wrap(const Executor& ex, T&& t);
Returns:
executor_wrapper<decay_t<T>, Executor>(forward<T>(t), ex)
.
Remarks: This function shall not participate in overload resolution unless
is_executor<Executor>::value
istrue
.
template<class ExecutionContext, class CompletionToken> executor_wrapper<decay_t<T>, typename ExecutionContext::executor_type> wrap(ExecutionContext& ctx, T&& t);
Returns:
wrap(ctx.get_executor(), forward<T>(t))
.
Remarks: This function shall not participate in overload resolution unless
is_convertible<ExecutionContext&, execution_context&>::value
istrue
.
namespace std { namespace experimental { inline namespace network_v1 { template<class Executor> class executor_work { public: // types: typedef Executor executor_type; // construct / copy / destroy: explicit executor_work(const executor_type& ex) noexcept; executor_work(const executor_work& other) noexcept; executor_work(executor_work&& other) noexcept; executor_work& operator=(const executor_work&) = delete; ~executor_work(); // executor work observers: executor_type get_executor() const noexcept; bool owns_work() const noexcept; // executor work modifiers: void reset() noexcept; private: Executor ex_; // exposition only bool owns_; // exposition only }; } // inline namespace network_v1 } // namespace experimental } // namespace std
explicit executor_work(const executor_type& ex) noexcept;
Effects: Constructs an object of class
executor_work
, initializingex_
withex
, and then performingex_.on_work_started()
.
Postconditions:
ex == ex_
andowns_ == true
.
executor_work(const executor_work& other) noexcept;
Effects: Constructs an object of class
executor_work
, initializingex_
withother.ex_
. Ifother.owns_ == true
, performsex_.on_work_started()
.
Postconditions:
ex_ == other.ex_
andowns_ == other.owns_
.
executor_work(executor_work&& other) noexcept;
Effects: Constructs an object of class
executor_work
, initializingex_
withstd::move(other.ex_)
andowns_
withother.owns_
, and setsother.owns_
tofalse
.
~executor_work();
Effects: If
owns_
istrue
, performsex_.on_work_finished()
.
executor_type get_executor() const noexcept;
Returns:
ex_
.
bool owns_work() const noexcept;
Returns:
owns_
.
void reset() noexcept;
Effects: If
owns_
istrue
, performsex_.on_work_finished()
.
Postconditions:
owns_ == false
.
template<class Executor> executor_work<Executor> make_work(const Executor& ex);
Returns:
executor_work<Executor>(ex)
.
Remarks: This function shall not participate in overload resolution unless
is_executor<Executor>::value
istrue
.
template<class ExecutionContext> executor_work<typename ExecutionContext::executor_type> make_work(ExecutionContext& ctx);
Returns: An object of type
executor_work<typename ExecutionContext::executor_type>
initialized with the result ofctx.get_executor()
.
Remarks: This function shall not participate in overload resolution unless
is_convertible<ExecutionContext&, execution_context&>::value
istrue
.
template<class T> executor_work<associated_executor_t<T>> make_work(const T& t);
Returns: An object of type
executor_work<associated_executor_t<T>>
initialized with the result ofassociated_executor<T>::get(t)
.
Remarks: This function shall not participate in overload resolution unless
is_executor<T>::value
isfalse
andis_convertible<T&, execution_context&>::value
isfalse
.
template<class T, class Executor> executor_work<associated_executor_t<T, Executor>> make_work(const T& t, const Executor& ex);
Returns: An object of type
executor_work<associated_executor_t<T, Executor>>
initialized with the result ofassociated_executor<T, Executor>::get(t, ex)
.
Remarks: This function shall not participate in overload resolution unless
is_executor<Executor>::value
istrue
.
template<class T, class ExecutionContext> executor_work<associated_executor_t<T, typename ExecutionContext::executor_type>> make_work(const T& t, ExecutionContext& ctx);
Returns: An object of type
executor_work<associated_executor_t<T, typename ExecutionContext::executor_type>>
initialized with the result ofassociated_executor<T, typename ExecutionContext::executor_type>::get(t, ctx.get_executor())
.
Remarks: This function shall not participate in overload resolution unless
is_convertible<ExecutionContext&, execution_context&>::value
istrue
.
Class system_executor
represents
a set of rules where function objects are permitted to execute on any thread.
namespace std { namespace experimental { inline namespace network_v1 { class system_executor { public: // constructors: system_executor() {} // executor operations: execution_context& context() noexcept; void on_work_started() noexcept {} void on_work_finished() noexcept {} template<class Func, class ProtoAllocator> void dispatch(Func&& f, const ProtoAllocator& a); template<class Func, class ProtoAllocator> void post(Func&& f, const ProtoAllocator& a); template<class Func, class ProtoAllocator> void defer(Func&& f, const ProtoAllocator& a); }; bool operator==(const system_executor&, const system_executor&) noexcept; bool operator!=(const system_executor&, const system_executor&) noexcept; } // inline namespace network_v1 } // namespace experimental } // namespace std
Class system_executor
satisfies
the Executor requirements.
To satisfy the executor requirements for the post
and defer
member functions,
the system executor may create thread
objects to run the submitted function objects. If the program is terminated
by any means, and there remain unexecuted functions objects that have been
submitted using post
or
defer
, these function objects
are discarded without calling them.
execution_context& context() noexcept;
Returns: A reference to an object with static storage duration whose type is derived from
execution_context
.
template<class Func, class ProtoAllocator> void dispatch(Func&& f, const ProtoAllocator& a);
Effects: Equivalent to
DECAY_COPY
(forward<Func>(f))()
.
template<class Func, class ProtoAllocator> void post(Func&& f, const ProtoAllocator& a); template<class Func, class ProtoAllocator> void defer(Func&& f, const ProtoAllocator& a);
Effects: Creates an object
f1
initialized withDECAY_COPY
(forward<Func>(f))
, and callsf1
as if in a thread of execution represented by athread
object. Any exception propagated from the execution ofDECAY_COPY
(forward<Func>(f))()
shall result in a call tostd::terminate
.
[async.system.exec.comparisons]
bool operator==(const system_executor&, const system_executor&) noexcept;
Returns:
true
.
bool operator!=(const system_executor&, const system_executor&) noexcept;
Returns:
false
.
An exception of type bad_executor
is thrown by executor
member
functions dispatch
, post
, and defer
when the executor object has no target.
namespace std { namespace experimental { inline namespace network_v1 { class bad_executor : public exception { public: // constructor: bad_executor() noexcept; }; } // inline namespace network_v1 } // namespace experimental } // namespace std bad_executor() noexcept;
Effects: constructs a
bad_executor
object.
Postconditions:
what()
returns an implementation-defined NTBS.
The executor
class provides
a polymorphic wrapper for types that satisfy the Executor
requirements.
namespace std { namespace experimental { inline namespace network_v1 { class executor { public: // construct / copy / destroy: executor() noexcept; executor(nullptr_t) noexcept; executor(const executor& e) noexcept; executor(executor&& e) noexcept; template<class Executor> executor(Executor e); template<class Executor, class ProtoAllocator> executor(allocator_arg_t, const ProtoAllocator& a, Executor e); executor& operator=(const executor& e) noexcept; executor& operator=(executor&& e) noexcept; executor& operator=(nullptr_t) noexcept; template<class Executor> executor& operator=(Executor e); ~executor(); // executor modifiers: void swap(executor& other) noexcept; template<class Executor, class ProtoAllocator> void assign(Executor e, const ProtoAllocator& a); // executor operations: execution_context& context() noexcept; void on_work_started() noexcept; void on_work_finished() noexcept; template<class Func, class ProtoAllocator> void dispatch(Func&& f, const ProtoAllocator& a); template<class Func, class ProtoAllocator> void post(Func&& f, const ProtoAllocator& a); template<class Func, class ProtoAllocator> void defer(Func&& f, const ProtoAllocator& a); // executor capacity: explicit operator bool() const noexcept; // executor target access: const type_info& target_type() const noexcept; template<class Executor> Executor* target() noexcept; template<class Executor> const Executor* target() const noexcept; }; template<> struct is_executor<executor> : true_type {}; // executor comparisons: bool operator==(const executor& a, const executor& b) noexcept; bool operator==(const executor& e, nullptr_t) noexcept; bool operator==(nullptr_t, const executor& e) noexcept; bool operator!=(const executor& a, const executor& b) noexcept; bool operator!=(const executor& e, nullptr_t) noexcept; bool operator!=(nullptr_t, const executor& e) noexcept; // executor specialized algorithms: void swap(executor& a, executor& b) noexcept; } // inline namespace network_v1 } // namespace experimental template<class Allocator> struct uses_allocator<experimental::network_v1::executor, Allocator> : true_type {}; } // namespace std
The target is the executor object that is held by
the wrapper. The executor
type itself meets the requirements for an Executor.
[Note: To meet the noexcept
requirements for executor copy constructors and move constructors, implementations
may share a target between two or more executor
objects. —end note]
executor() noexcept;
Postconditions:
!*this
.
executor(nullptr_t) noexcept;
Postconditions:
!*this
.
executor(const executor& e) noexcept;
Postconditions:
!*this
if!e
; otherwise,*this
targetse.target()
or a copy ofe.target()
.
executor(executor&& e) noexcept;
Effects: If
!e
,*this
has no target; otherwise, movese.target()
or move-constructs the target ofe
into the target of*this
, leavinge
in a valid state with an unspecified value.
template<class Executor> executor(Executor e);
Effects:
*this
targets a copy ofe
initialized withstd::move(e)
.
template<class Executor, class ProtoAllocator> executor(allocator_arg_t, const ProtoAllocator& a, Executor e);
Effects:
*this
targets a copy ofe
initialized withstd::move(e)
.
A copy of the allocator argument is used to allocate memory, if necessary, for the internal data structures of the constructed
executor
object.
executor& operator=(const executor& e) noexcept;
Effects:
executor(e).swap(*this)
.
Returns:
*this
.
executor& operator=(executor&& e) noexcept;
Effects: Replaces the target of
*this
with the target ofe
, leavinge
in a valid state with an unspecified value.
Returns:
*this
.
executor& operator=(nullptr_t) noexcept;
Effects:
executor(nullptr).swap(*this)
.
Returns:
*this
.
template<class Executor> executor& operator=(Executor e);
Effects:
executor(std::move(e)).swap(*this)
.
Returns:
*this
.
~executor();
Effects: If
*this != nullptr
, releases shared ownership of, or destroys, the target of*this
.
void swap(executor& other) noexcept;
Effects: Interchanges the targets of
*this
andother
.
template<class Executor, class ProtoAllocator> void assign(Executor e, const ProtoAllocator& a);
Effects:
executor(allocator_arg, a, std::move(e)).swap(*this)
.
execution_context& context() noexcept;
Requires:
*this != nullptr
.
Returns:
e.context()
, wheree
is the target object of*this
.
void on_work_started() noexcept;
Requires:
*this != nullptr
.
Effects:
e.on_work_started()
, wheree
is the target object of*this
.
void on_work_finished() noexcept;
Requires:
*this != nullptr
.
Effects:
e.on_work_finished()
, wheree
is the target object of*this
.
template<class Func, class ProtoAllocator> void dispatch(Func&& f, const ProtoAllocator& a);
Effects:
e.dispatch(g, a)
, wheree
is the target object of*this
, andg
is a function object of unspecified type that, when called asg()
, performsDECAY_COPY(f)()
.
template<class Func, class ProtoAllocator> void post(Func&& f, const ProtoAllocator& a);
Effects:
e.post(g, a)
, wheree
is the target object of*this
, andg
is a function object of unspecified type that, when called asg()
, performsDECAY_COPY(f)()
.
template<class Func, class ProtoAllocator> void defer(Func&& f, const ProtoAllocator& a);
Effects:
e.defer(g, a)
, wheree
is the target object of*this
, andg
is a function object of unspecified type that, when called asg()
, performsDECAY_COPY(f)()
.
explicit operator bool() const noexcept;
Returns:
true
if*this
has a target, otherwisefalse
.
const type_info& target_type() const noexcept;
Returns: If
*this
has a target of typeT
,typeid(T)
; otherwise,typeid(void)
.
template<class Executor> Executor* target() noexcept; template<class Executor> const Executor* target() const noexcept;
Returns: If
target_type() == typeid(Executor)
a pointer to the stored executor target; otherwise a null pointer value.
bool operator==(const executor& a, const executor& b) noexcept;
Returns:
—true
if!a
and!b
;
—true
ifa
andb
share a target;
—true
ife
andf
are the same type ande == f
, wheree
is the target ofa
andf
is the target ofb
;
— otherwisefalse
.
bool operator==(const executor& e, nullptr_t) noexcept; bool operator==(nullptr_t, const executor& e) noexcept;
Returns:
!e
.
bool operator!=(const executor& a, const executor& b) noexcept;
Returns:
!(a == b)
.
bool operator!=(const executor& e, nullptr_t) noexcept; bool operator!=(nullptr_t, const executor& e) noexcept;
Returns:
(bool) e
.
void swap(executor& a, executor& b) noexcept;
Effects:
a.swap(b)
.
template<class CompletionToken> DEDUCED dispatch(CompletionToken&& token);
Let the type
CompletionHandler
be the completion handler type determined by performingcompletion_handler_type_t<CompletionToken, void()>
.
Requires: The type
CompletionHandler
must satisfy theMoveConstructible
requirements (C++ Std, [moveconstructible]) and be callable with zero arguments.
Effects:
— Constructs a function objectcompletion_handler
of typeCompletionHandler
, initialized ascompletion_handler(forward<CompletionToken>(token))
.
— Constructs an objectresult
of typeasync_result<CompletionHandler>
, initializing the object asresult(completion_handler)
.
— Obtains the completion handler's associated executor objectex
by performingget_associated_executor(completion_handler)
.
— Obtains the completion handler's associated allocator objectalloc
by performingget_associated_allocator(completion_handler)
.
— Performsex.dispatch(std::move(completion_handler), alloc)
.
Returns:
result.get()
.
template<class Executor, class CompletionToken> DEDUCED dispatch(const Executor& ex, CompletionToken&& token);
Let the type
CompletionHandler
be the completion handler type determined by performingcompletion_handler_type_t<CompletionToken, void()>
.
Requires: The type
CompletionHandler
must satisfy theMoveConstructible
requirements (C++ Std, [moveconstructible]) and be callable with zero arguments.
Effects:
— Constructs a function objectcompletion_handler
of typeCompletionHandler
, initialized ascompletion_handler(forward<CompletionToken>(token))
.
— Constructs an objectresult
of typeasync_result<CompletionHandler>
, initializing the object asresult(completion_handler)
.
— Obtains the completion handler's associated executor objectex1
by performingget_associated_executor(completion_handler)
.
— Creates a work objectw
by performingmake_work(ex1)
.
— Obtains the completion handler's associated allocator objectalloc
by performingget_associated_allocator(completion_handler)
.
— Constructs a function objectf
with a function call operator that performsex1.dispatch(std::move(completion_handler), alloc)
followed byw.reset()
.
— Performsex.dispatch(std::move(f), alloc)
.
Returns:
result.get()
.
Remarks: This function shall not participate in overload resolution unless
is_executor<Executor>::value
istrue
.
template<class ExecutionContext, class CompletionToken> DEDUCED dispatch(ExecutionContext& ctx, CompletionToken&& token);
Returns:
std::experimental::dispatch(ctx.get_executor(), forward<CompletionToken>(token))
.
Remarks: This function shall not participate in overload resolution unless
is_convertible<ExecutionContext&, execution_context&>::value
istrue
.
template<class CompletionToken> DEDUCED post(CompletionToken&& token);
Let the type
CompletionHandler
be the completion handler type determined by performingcompletion_handler_type_t<CompletionToken, void()>
.
Requires: The type
CompletionHandler
must satisfy theMoveConstructible
requirements (C++ Std, [moveconstructible]) and be callable with zero arguments.
Effects:
— Constructs a function objectcompletion_handler
of typeCompletionHandler
, initialized ascompletion_handler(forward<CompletionToken>(token))
.
— Constructs an objectresult
of typeasync_result<CompletionHandler>
, initializing the object asresult(completion_handler)
.
— Obtains the completion handler's associated executor objectex
by performingget_associated_executor(completion_handler)
.
— Obtains the completion handler's associated allocator objectalloc
by performingget_associated_allocator(completion_handler)
.
— Performsex.post(std::move(completion_handler), alloc)
.
Returns:
result.get()
.
template<class Executor, class CompletionToken> DEDUCED post(const Executor& ex, CompletionToken&& token);
Let the type
CompletionHandler
be the completion handler type determined by performingcompletion_handler_type_t<CompletionToken, void()>
.
Requires: The type
CompletionHandler
must satisfy theMoveConstructible
requirements (C++ Std, [moveconstructible]) and be callable with zero arguments.
Effects:
— Constructs a function objectcompletion_handler
of typeCompletionHandler
, initialized ascompletion_handler(forward<CompletionToken>(token))
.
— Constructs an objectresult
of typeasync_result<CompletionHandler>
, initializing the object asresult(completion_handler)
.
— Obtains the completion handler's associated executor objectex1
by performingget_associated_executor(completion_handler)
.
— Creates a work objectw
by performingmake_work(ex1)
.
— Obtains the completion handler's associated allocator objectalloc
by performingget_associated_allocator(completion_handler)
.
— Constructs a function objectf
with a function call operator that performsex1.dispatch(std::move(completion_handler), alloc)
followed byw.reset()
.
— Performsex.post(std::move(f), alloc)
.
Returns:
result.get()
.
Remarks: This function shall not participate in overload resolution unless
is_executor<Executor>::value
istrue
.
template<class ExecutionContext, class CompletionToken> DEDUCED post(ExecutionContext& ctx, CompletionToken&& token);
Returns:
std::experimental::post(ctx.get_executor(), forward<CompletionToken>(token))
.
Remarks: This function shall not participate in overload resolution unless
is_convertible<ExecutionContext&, execution_context&>::value
istrue
.
template<class CompletionToken> DEDUCED defer(CompletionToken&& token);
Let the type
CompletionHandler
be the completion handler type determined by performingcompletion_handler_type_t<CompletionToken, void()>
.
Requires: The type
CompletionHandler
must satisfy theMoveConstructible
requirements (C++ Std, [moveconstructible]) and be callable with zero arguments.
Effects:
— Constructs a function objectcompletion_handler
of typeCompletionHandler
, initialized ascompletion_handler(forward<CompletionToken>(token))
.
— Constructs an objectresult
of typeasync_result<CompletionHandler>
, initializing the object asresult(completion_handler)
.
— Obtains the completion handler's associated executor objectex
by performingget_associated_executor(completion_handler)
.
— Obtains the completion handler's associated allocator objectalloc
by performingget_associated_allocator(completion_handler)
.
— Performsex.defer(std::move(completion_handler), alloc)
.
Returns:
result.get()
.
template<class Executor, class CompletionToken> DEDUCED defer(const Executor& ex, CompletionToken&& token);
Let the type
CompletionHandler
be the completion handler type determined by performingcompletion_handler_type_t<CompletionToken, void()>
.
Requires: The type
CompletionHandler
must satisfy theMoveConstructible
requirements (C++ Std, [moveconstructible]) and be callable with zero arguments.
Effects:
— Constructs a function objectcompletion_handler
of typeCompletionHandler
, initialized ascompletion_handler(forward<CompletionToken>(token))
.
— Constructs an objectresult
of typeasync_result<CompletionHandler>
, initializing the object asresult(completion_handler)
.
— Obtains the completion handler's associated executor objectex1
by performingget_associated_executor(completion_handler)
.
— Creates a work objectw
by performingmake_work(ex1)
.
— Obtains the completion handler's associated allocator objectalloc
by performingget_associated_allocator(completion_handler)
.
— Constructs a function objectf
with a function call operator that performsex1.dispatch(std::move(completion_handler), alloc)
followed byw.reset()
.
— Performsex.defer(std::move(f), alloc)
.
Returns:
result.get()
.
Remarks: This function shall not participate in overload resolution unless
is_executor<Executor>::value
istrue
.
template<class ExecutionContext, class CompletionToken> DEDUCED defer(ExecutionContext& ctx, CompletionToken&& token);
Returns:
std::experimental::defer(ctx.get_executor(), forward<CompletionToken>(token))
.
Remarks: This function shall not participate in overload resolution unless
is_convertible<ExecutionContext&, execution_context&>::value
istrue
.
The class template strand
is a wrapper around an object of type Executor
satisfying the Executor requirements.
namespace std { namespace experimental { inline namespace network_v1 { template<class Executor> class strand { public: // types: typedef Executor inner_executor_type; // construct / copy / destroy: strand(); explicit strand(Executor ex); strand(const strand& other) noexcept; strand(strand&& other) noexcept; template<class OtherExecutor> strand(const strand<OtherExecutor>& other) noexcept; template<class OtherExecutor> strand(strand<OtherExecutor>&& other) noexcept; strand& operator=(const strand& other) noexcept; strand& operator=(strand&& other) noexcept; template<class OtherExecutor> strand& operator=(const strand<OtherExecutor>& other) noexcept; template<class OtherExecutor> strand& operator=(strand<OtherExecutor>&& other) noexcept; ~strand(); // strand operations: inner_executor_type get_inner_executor() const noexcept; bool running_in_this_thread() const noexcept; execution_context& context() noexcept; void on_work_started() noexcept; void on_work_finished() noexcept; template<class Func, class ProtoAllocator> void dispatch(Func&& f, const ProtoAllocator& a); template<class Func, class ProtoAllocator> void post(Func&& f, const ProtoAllocator& a); template<class Func, class ProtoAllocator> void defer(Func&& f, const ProtoAllocator& a); private: Executor inner_ex_; // exposition only }; bool operator==(const strand<Executor>& a, const strand<Executor>& b); bool operator!=(const strand<Executor>& a, const strand<Executor>& b); } // inline namespace network_v1 } // namespace experimental } // namespace std
strand<Executor>
satisfies the Executor requirements.
A strand provides guarantees of ordering and non-concurrency. Given:
— strand objects s1
and
s2
such that s1 == s2
— a function object f1
added
to the strand s1
using
post
or defer
,
or using dispatch
when
s1.running_in_this_thread()
== false
— a function object f2
added
to the strand s2
using
post
or defer
,
or using dispatch
when
s2.running_in_this_thread()
== false
then the implementation invokes f1
and f2
such that:
— the invocation of f1
is
not concurrent with the invocation of f2
— the invocation of f1
synchronizes
with the invocation of f2
.
Furthermore, if the addition of f1
happens before the addition of f2
,
then the invocation of f1
happens before the invocation of f2
.
All member functions, except for the assignment operators and the destructor,
do not introduce data races on *this
, including its ordered, non-concurrent
state. Additionally, constructors and assignment operators do not introduce
data races on lvalue arguments.
If any function f
executed
by the strand throws an exception, the subsequent strand state is as if
f
had exited without throwing
an exception.
strand();
Effects: Constructs an object of class
strand<Executor>
that represents a unique ordered, non-concurrent state. Initializesinner_ex_
withinner_ex_()
.
Remarks: This overload shall not participate in overload resolution unless
Executor
satisfies theDefaultConstructible
requirements (C++ Std, [defaultconstructible]).
explicit strand(Executor ex);
Effects: Constructs an object of class
strand<Executor>
that represents a unique ordered, non-concurrent state. Initializesinner_ex_
asinner_ex_(ex)
.
strand(const strand& other) noexcept;
Effects: Constructs an object of class
strand<Executor>
. Initializesinner_ex_
asinner_ex_(other.inner_ex_)
.
Postconditions:
—*this == other
—get_inner_executor() == other.get_inner_executor()
strand(strand&& other) noexcept;
Effects: Constructs an object of class
strand<Executor>
. Initializesinner_ex_
withinner_ex_(std::move(other.inner_ex_))
.
Postconditions:
—*this
is equal to the prior value ofother
—get_inner_executor() == other.get_inner_executor()
template<class OtherExecutor> strand(const strand<OtherExecutor>& other) noexcept;
Requires:
OtherExecutor
is convertible toExecutor
.
Effects: Constructs an object of class
strand<Executor>
. Initializesinner_ex_
withinner_ex_(other.inner_ex_)
.
Postconditions:
*this == other
.
template<class OtherExecutor> strand(strand<OtherExecutor>&& other) noexcept;
Requires:
OtherExecutor
is convertible toExecutor
.
Effects: Constructs an object of class
strand<Executor>
. Initializesinner_ex_
withinner_ex_(std::move(other.inner_ex_))
.
Postconditions:
*this
is equal to the prior value ofother
.
strand& operator=(const strand& other) noexcept;
Requires:
Executor
isAssignable
(C++ Std [assignable]).
Postconditions:
—*this == other
—get_inner_executor() == other.get_inner_executor()
Returns:
*this
.
strand& operator=(strand&& other) noexcept;
Requires:
Executor
isAssignable
(C++ Std [assignable]).
Postconditions:
—*this
is equal to the prior value ofother
—get_inner_executor() == other.get_inner_executor()
Returns:
*this
.
template<class OtherExecutor> strand& operator=(const strand<OtherExecutor>& other) noexcept;
Requires:
OtherExecutor
is convertible toExecutor
.Executor
isAssignable
(C++ Std [assignable]).
Effects: Assigns
other.inner_ex_
toinner_ex_
.
Postconditions:
*this == other
.
Returns:
*this
.
template<class OtherExecutor> strand& operator=(strand<OtherExecutor>&& other) noexcept;
Requires:
OtherExecutor
is convertible toExecutor
.Executor
isAssignable
(C++ Std [assignable]).
Effects: Assigns
std::move(other.inner_ex_)
toinner_ex_
.
Postconditions:
*this
is equal to the prior value ofother
.
Returns:
*this
.
~strand();
Effects: Destroys an object of class
strand<Executor>
. After this destructor completes, objects that were added to the strand but have not yet been executed will be executed in a way that meets the guarantees of ordering and non-concurrency.
inner_executor_type get_inner_executor() const noexcept;
Returns:
inner_ex_
.
bool running_in_this_thread() const noexcept;
Returns:
true
if the current thread of execution is running a function that was submitted to the strand, or to any other strand objects
such thats == *this
, usingdispatch
,post
ordefer
; otherwisefalse
. [Note: That is, the current thread of execution's call chain includes a function that was submitted to the strand. —end note]
execution_context& context() noexcept;
Returns:
inner_ex_.context()
.
void on_work_started() noexcept;
Effects: Calls
inner_ex_.on_work_started()
.
void on_work_finished() noexcept;
Effects: Calls
inner_ex_.on_work_finished()
.
template<class Func, class ProtoAllocator> void dispatch(Func&& f, const ProtoAllocator& a);
Effects: If
running_in_this_thread() == true
, callsDECAY_COPY
(forward<Func>(f))()
. [Note: Iff
exits via an exception, the exception propagates to the caller ofdispatch()
. —end note] Otherwise, requests invocation off
, as if by forwarding the function objectf
and allocatora
to the executorinner_ex_
, such that the guarantees of ordering and non-concurrency are met.
template<class Func, class ProtoAllocator> void post(Func&& f, const ProtoAllocator& a);
Effects: Requests invocation of
f
, as if by forwarding the function objectf
and allocatora
to the executorinner_ex_
, such that the guarantees of ordering and non-concurrency are met.
template<class Func, class ProtoAllocator> void defer(Func&& f, const ProtoAllocator& a);
Effects: Requests invocation of
f
, as if by forwarding the function objectf
and allocatora
to the executorinner_ex_
, such that the guarantees of ordering and non-concurrency are met.
bool operator==(const strand<Executor>& a, const strand<Executor>& b);
Returns:
true
, if the strand objects share the same ordered, non-concurrent state; otherwisefalse
.
bool operator!=(const strand<Executor>& a, const strand<Executor>& b);
Returns:
!(a == b)
.
The class template use_future_t
defines a set of completion
token types for use with asynchronous operations.
namespace std { namespace experimental { inline namespace network_v1 { template<class ProtoAllocator = allocator<void>> class use_future_t { public: // use_future_t types: typedef ProtoAllocator allocator_type; // use_future_t members: constexpr use_future_t() noexcept; explicit use_future_t(const allocator_type& a) noexcept; template<class OtherProtoAllocator> use_future_t<OtherProtoAllocator> rebind(const OtherProtoAllocator& a) const noexcept; allocator_type get_allocator() const noexcept; }; } // inline namespace network_v1 } // namespace experimental } // namespace std
constexpr use_future_t() noexcept;
Effects: Constructs a
use_future_t
with a default-constructed allocator.
explicit use_future_t(const allocator_type& a) noexcept;
Effects: Constructs an object of type
use_future_t
.
Postconditions:
get_allocator() == a
.
template<class OtherProtoAllocator> use_future_t<OtherProtoAllocator> rebind(const OtherProtoAllocator& a) const noexcept;
Returns: A
use_future_t
object whereget_allocator() == a
.
allocator_type get_allocator() const noexcept;
Returns: The associated allocator object.
template<class ProtoAllocator, class R, class... Args> struct completion_handler_type<use_future_t<ProtoAllocator>, R(Args...)> { typedef see below type; };
Let F
be the nested function
object type type
.
An object t1
of type
F
is an asynchronous
provider with an associated shared state (C++Std, [futures.state]). The
type F
provides F::operator()
such that the expression t1(declval<Args>()...)
is well formed.
The implementation shall specialize associated_executor
for F
. For function objects
executed using the associated executor's dispatch()
, post()
or defer()
functions, any exception thrown is
caught by the executor and stored in the associated shared state.
async_result<F>
is valid and when an object r1
of type async_result<F>
is constructed from t1
, the expression r1.get()
returns a future with the same shared
state as t1
.
The semantics of async_result::type
and F::operator()
are defined in the table below. In this table, N
is
the value of sizeof...(Args)
;
let i
be in the range [0
..N
)
and let Ti
be the i
th
type in Args
; let Ui
be decay_t<Ti>
for each
type Ti
in Args
;
and let ai
be the i
th
argument to F::operator()
.
Table 8. completion_handler_type<use_future_t<ProtoAllocator>, R(Args...)>::type semantics
|
|
|
|
---|---|---|---|
0 |
|
Makes the shared state ready. | |
1 |
|
|
If |
1 |
|
|
If |
1 |
all other types |
|
Atomically stores |
2 |
|
|
If |
2 |
|
|
If |
2 |
all other types |
|
Atomically stores the result of |
>2 |
|
|
If |
>2 |
|
|
If |
>2 |
all other types |
|
Atomically stores the result of |
[async.packaged.task.specializations]
namespace std { namespace experimental { inline namespace network_v1 { template<class R, class... Args> class async_result<packaged_task<R(Args...)>> { public: typedef future<R> type; explicit async_result(packaged_task<R(Args...)>& t); async_result(const async_result&) = delete; async_result& operator=(const async_result&) = delete; type get(); private: type future_; // exposition only }; } // inline namespace network_v1 } // namespace experimental } // namespace std explicit async_result(packaged_task<R(Args...)>& t);
Effects: Initializes
future_
witht.get_future()
.
type get();
Returns:
std::move(future_)
.
Class template packaged_handler
is an adapter to enable the use of packaged tasks with asynchronous operations
as a completion handler with an associated allocator.
namespace std { namespace experimental { inline namespace network_v1 { template<class, class> class packaged_handler; // undefined template<class R, class... Args, class ProtoAllocator> class packaged_handler<R(Args...), ProtoAllocator> { public: // packaged_handler types: typedef ProtoAllocator allocator_type; // packaged_handler construct / copy / assign: template<class Func> explicit packaged_handler(packaged_token<Func, ProtoAllocator>&& token); packaged_handler(const packaged_handler&) = delete; packaged_handler(packaged_handler&& rhs) noexcept; packaged_handler& operator=(const packaged_handler&) = delete; packaged_handler& operator=(packaged_handler&& rhs) noexcept; // packaged_handler operations: allocator_type get_allocator() const noexcept; future<R> get_future(); void operator()(Args... args); private: packaged_task<R(Args...)> task_; // exposition only ProtoAllocator allocator_; // exposition only }; template<class Signature, class ProtoAllocator> class async_result<packaged_handler<Signature, ProtoAllocator>>; } // inline namespace network_v1 } // namespace experimental } // namespace std
[async.package.handler.members]
template<class Func> explicit packaged_handler(packaged_token<Func, ProtoAllocator>&& token);
Effects: Initializes
task_
withstd::move(token.f_)
and initializesallocator_
withtoken.allocator_
.
packaged_handler(packaged_handler&& rhs) noexcept;
Effects: Initializes
task_
withstd::move(rhs.task_)
and initializesallocator_
withstd::move(rhs.allocator_)
.
packaged_handler& operator=(packaged_handler&& rhs) noexcept;
Effects: Performs
task_ = std::move(rhs.task_)
andallocator_ = std::move(rhs.allocator_)
.
Returns:
*this
.
allocator_type get_allocator() const noexcept;
Returns:
allocator_
.
future<R> get_future();
Returns:
task_.get_future()
.
void operator()(Args... args);
Effects: Calls
task_(forward<Args>(args)...)
.
[async.package.handler.async.result]
namespace std { namespace experimental { inline namespace network_v1 { template<class R, class... Args, class ProtoAllocator> class async_result<packaged_handler<R(Args...), ProtoAllocator>> { public: typedef future<R> type; explicit async_result(packaged_task<R(Args...)>& t); async_result(const async_result&) = delete; async_result& operator=(const async_result&) = delete; type get(); private: type future_; // exposition only }; } // inline namespace network_v1 } // namespace experimental } // namespace std
The implementation provides a partial specialization of async_result
that meets the async_result
specialization requirements.
explicit async_result(packaged_handler<Signature, ProtoAllocator>& h);
Effects: Initializes
future_
witht.get_future()
.
type get();
Returns:
std::move(future_)
.
Class template packaged_handler
is an adapter to enable the use of packaged tasks with asynchronous operations
as a completion token. The specification of the packaged task's Signature
is deferred until the completion
token is transformed into a completion handler.
namespace std { namespace experimental { inline namespace network_v1 { template<class Func, class ProtoAllocator = allocator<void>> class packaged_token { public: // packaged_token types: typedef ProtoAllocator allocator_type; // packaged_token constructors: explicit packaged_token(Func f); packaged_token(Func f, const allocator_type& a); // packaged_token operations: allocator_type get_allocator() const noexcept; private: Func f_; // exposition only ProtoAllocator allocator_; // exposition only }; template<class Func, class ProtoAllocator, class R, class... Args> struct completion_handler_type<packaged_token<Func, ProtoAllocator>, R(Args...)> { typedef packaged_handler<result_of_t<Func(Args...)>(Args...), ProtoAllocator> type; }; } // inline namespace network_v1 } // namespace experimental } // namespace std
explicit packaged_token(Func f);
Effects: Initializes
f_
withstd::move(f)
and default constructsallocator_
.
packaged_token(Func f, const allocator_type& a);
Effects: Initializes
f_
withstd::move(f)
andallocator_
witha
.
allocator_type get_allocator() const noexcept;
Returns:
allocator_
.
template<class Func, class ProtoAllocator = allocator<void>> packaged_token<decay_t<Func>, ProtoAllocator> package( Func&& f, const ProtoAllocator& a = ProtoAllocator());
Returns:
packaged_token<decay_t<Func>, ProtoAllocator>(forward<Func>(f), a)
.
namespace std { namespace experimental { inline namespace network_v1 { class io_service; } // inline namespace network_v1 } // namespace experimental } // namespace std
namespace std { namespace experimental { inline namespace network_v1 { class io_service : public execution_context { public: // types: class executor_type; // construct / copy / destroy: io_service(); explicit io_service(int concurrency_hint); io_service(const io_service&) = delete; io_service& operator=(const io_service&) = delete; // io_service operations: executor_type get_executor() noexcept; size_t run(); template<class Rep, class Period> size_t run_for(const chrono::duration<Rep, Period>& rel_time); template<class Clock, class Duration> size_t run_until(const chrono::time_point<Clock, Duration>& abs_time); size_t run_one(); template<class Rep, class Period> size_t run_one_for(const chrono::duration<Rep, Period>& rel_time); template<class Clock, class Duration> size_t run_one_until(const chrono::time_point<Clock, Duration>& abs_time); size_t poll(); size_t poll_one(); void stop(); bool stopped() const noexcept; void restart(); }; } // inline namespace network_v1 } // namespace experimental } // namespace std
The class io_service
satsifies
the ExecutionContext type
requirements.
The io_service
functions
run
, run_for
,
run_until
, run_one
, run_one_for
,
run_one_until
, poll
, or poll_one
must be called for the io_service
to perform asynchronous
operations on behalf of a C++ program. Notification that an asynchronous
operation has completed is delivered by execution of the associated handler
function object, as determined by the requirements for asynchronous
operations.
For an object of type io_service
,
outstanding work is defined as the sum of:
— the total number of calls to the on_work_started
function, less the total number of calls to the on_work_finished
function, to any executor of the io_service
.
— the number of function objects that have been added to the io_service
via any executor of the io_service
, but not yet executed; and
— the number of function objects that are currently being executed by the
io_service
.
If at any time the outstanding work falls to 0
,
the io_service
is stopped
as if by stop()
.
The io_service
member functions
get_executor
, run
, run_for
,
run_until
, run_one
, run_one_for
,
run_one_until
, poll
, poll_one
,
stop
, and stopped
, and the io_service::executor_type
copy constructors, member functions and comparison operators, shall not
introduce data races as a result of concurrent calls to those functions
from different threads of execution. [Note: The restart
member function is excluded from
these thread safety requirements. —end note]
[io_service.io_service.members]
io_service(); explicit io_service(int concurrency_hint);
Effects: Creates an object of class
io_service
.
Remarks: The
concurrency_hint
parameter is a suggestion to the implementation on the number of threads that should process asynchronous operations and execute function objects.
executor_type get_executor() noexcept;
Returns: An executor that may be used for submitting function objects to the
io_service
.
size_t run();
Requires: Must not be called from a thread that is currently calling one of
run
,run_for
,run_until
,run_one
,run_one_for
,run_one_until
,poll
, orpoll_one
.
Effects: Equivalent to:
size_t n = 0; while (run_one()) if (n != numeric_limits<size_t>::max()) ++n;
Returns:
n
.
template<class Rep, class Period> size_t run_for(const chrono::duration<Rep, Period>& rel_time);
Effects: Equivalent to:
return run_until(chrono::steady_clock::now() + rel_time);
template<class Clock, class Duration> size_t run_until(const chrono::time_point<Clock, Duration>& abs_time);
Effects: Equivalent to:
size_t n = 0; while (run_one_until(abs_time)) if (n != numeric_limits<size_t>::max()) ++n;
Returns:
n
.
size_t run_one();
Requires: Must not be called from a thread that is currently calling one of
run
,run_for
,run_until
,run_one
,run_one_for
,run_one_until
,poll
, orpoll_one
.
Effects: If the
io_service
object has no outstanding work, performsstop()
. Otherwise, blocks while the io_service has outstanding work, or until theio_service
is stopped, or until one function object has been executed.
If an executed function object throws an exception, the exception propagates to the caller of
run_one()
. Theio_service
state is as if the function object had returned normally.
Returns:
1
if a function object was executed, otherwise0
.
Notes: This function may invoke additional function objects through nested calls to the
io_service
executor'sdispatch
member function. These do not count towards the return value.
template<class Rep, class Period> size_t run_one_for(const chrono::duration<Rep, Period>& rel_time);
Effects: Equivalent to:
return run_one_until(chrono::steady_clock::now() + rel_time);
template<class Clock, class Duration> size_t run_one_until(const chrono::time_point<Clock, Duration>& abs_time);
Effects: If the
io_service
object has no outstanding work, performsstop()
. Otherwise, blocks while the io_service has outstanding work, or until the expiration of the absolute timeout (C++ Std, [thread.req.timing]) specified byabs_time
, or until theio_service
is stopped, or until one function object has been executed.
If an executed function object throws an exception, the exception propagates to the caller of
run_one()
. Theio_service
state is as if the function object had returned normally.
Returns:
1
if a function object was executed, otherwise0
.
Notes: This function may invoke additional function objects through nested calls to the
io_service
executor'sdispatch
member function. These do not count towards the return value.
size_t poll();
Effects: Equivalent to:
size_t n = 0; while (poll_one()) if (n != numeric_limits<size_t>::max()) ++n;
Returns:
n
.
size_t poll_one();
Effects: If the
io_service
object has no outstanding work, performsstop()
. Otherwise, if there is a function object ready for immediate execution, executes it.
If an executed function object throws an exception, the exception propagates to the caller of
poll_one()
. Theio_service
state is as if the function object had returned normally.
Returns:
1
if a function object was invoked, otherwise0
.
Notes: This function may invoke additional function objects through nested calls to the
io_service
executor'sdispatch
member function. These do not count towards the return value.
void stop();
Effects: Stops the
io_service
. Concurrent calls torun
,run_for
,run_until
,run_one
,run_one_for
,run_one_until
,poll
orpoll_one
will end as soon as possible. If a call torun
,run_for
,run_until
,run_one
,run_one_for
,run_one_until
,poll
orpoll_one
is currently executing a function object, the call will end only after completion of that function object. The call tostop()
returns without waiting for concurrent calls torun
,run_for
,run_until
,run_one
,run_one_for
,run_one_until
,poll
orpoll_one
to complete.
Postconditions:
stopped() == true
.
[Note: When
stopped() == true
, subsequent calls torun
,run_for
,run_until
,run_one
,run_one_for
,run_one_until
,poll
orpoll_one
will exit immediately with a return value of0
, without executing any function objects. Anio_service
remains in the stopped state until a call torestart()
. —end note]
bool stopped() const noexcept;
Returns:
true
if theio_service
is stopped.
void restart();
Postconditions:
stopped() == false
.
namespace std { namespace experimental { inline namespace network_v1 { class io_service::executor_type { public: // construct / copy / destroy: executor_type(const executor_type& other) noexcept; executor_type(executor_type&& other) noexcept; executor_type& operator=(const executor_type& other) noexcept; executor_type& operator=(executor_type&& other) noexcept; // executor operations: bool running_in_this_thread() const noexcept; io_service& context() noexcept; void on_work_started() noexcept; void on_work_finished() noexcept; template<class Func, class ProtoAllocator> void dispatch(Func&& f, const ProtoAllocator& a); template<class Func, class ProtoAllocator> void post(Func&& f, const ProtoAllocator& a); template<class Func, class ProtoAllocator> void defer(Func&& f, const ProtoAllocator& a); }; bool operator==(const io_service::executor_type& a, const io_service::executor_type& b) noexcept; bool operator!=(const io_service::executor_type& a, const io_service::executor_type& b) noexcept; template<> struct is_executor<io_service::executor_type> : true_type {}; } // inline namespace network_v1 } // namespace experimental } // namespace std
io_service::executor_type
is a type satisfying Executor requirements. Objects of
type io_service::executor_type
are associated with an
io_service
, and function
objects submitted using the dispatch
,
post
, or defer
member functions will be executed
by the io_service
from
within the run
, run_for
, run_until
,
run_one
, run_one_for
, run_one_until
,
poll
or poll_one
functions.
executor_type(const executor_type& other) noexcept;
Postconditions:
*this == other
.
executor_type(executor_type&& other) noexcept;
Postconditions:
*this
is equal to the prior value ofother
.
executor_type& operator=(const executor_type& other) noexcept;
Postconditions:
*this == other
.
Returns:
*this
.
executor_type& operator=(executor_type&& other) noexcept;
Postconditions:
*this
is equal to the prior value ofother
.
Returns:
*this
.
bool running_in_this_thread() const noexcept;
Returns:
true
if the current thread of execution is running therun
,run_for
,run_until
,run_one
,run_one_for
,run_one_until
,poll
orpoll_one
function of the associatedio_service
object. [Note: That is, the current thread of execution's call chain includes a run function. —end note]
io_service& context() noexcept;
Returns: A reference to the associated
io_service
object.
void on_work_started() noexcept;
Effects: Increment the count of outstanding work associated with the
io_service
.
void on_work_finished() noexcept;
Effects: Decrement the count of outstanding work associated with the
io_service
.
template<class Func, class ProtoAllocator> void dispatch(Func&& f, const ProtoAllocator& a);
Effects: If
running_in_this_thread()
istrue
, callsDECAY_COPY
(forward<Func>(f))()
. [Note: Iff
exits via an exception, the exception propagates to the caller ofdispatch()
. —end note] Otherwise, callspost(forward<Func>(f), a)
.
template<class Func, class ProtoAllocator> void post(Func&& f, const ProtoAllocator& a);
Effects: Adds
f
to theio_service
.
template<class Func, class ProtoAllocator> void defer(Func&& f, const ProtoAllocator& a);
Effects: Adds
f
to theio_service
.
bool operator==(const io_service::executor_type& a, const io_service::executor_type& b) noexcept;
Returns:
addressof(a.context()) == addressof(b.context())
.
bool operator!=(const io_service::executor_type& a, const io_service::executor_type& b) noexcept;
Returns:
!(a == b)
.
[timer] This clause defines components for performing timer operations.
[Example: Performing a synchronous wait operation on a timer:
io_service i; steady_timer t(i); t.expires_after(seconds(5)); t.wait();
—end example]
[Example: Performing an asynchronous wait operation on a timer:
void handler(error_code ec) { ... } ... io_service i; steady_timer t(i); t.expires_after(seconds(5)); t.async_wait(handler); i.run();
—end example]
#include <chrono> namespace std { namespace experimental { inline namespace network_v1 { template<class Clock> struct wait_traits; template<class Clock, class WaitTraits = wait_traits<Clock>> class basic_waitable_timer; typedef basic_waitable_timer<chrono::system_clock> system_timer; typedef basic_waitable_timer<chrono::steady_clock> steady_timer; typedef basic_waitable_timer<chrono::high_resolution_clock> high_resolution_timer; } // inline namespace network_v1 } // namespace experimental } // namespace std
In the table below, X
denotes a wait traits class for a type Clock
meeting the Clock
requirements
(C++ Std [time.clock.req]); t
denotes a (possibly const) value of type Clock::time_point
;
and d
denotes a (possibly
const) value of type Clock::duration
.
Table 9. WaitTraits requirements
expression |
return type |
assertion/note |
---|---|---|
|
|
Returns a |
|
|
Returns a |
namespace std { namespace experimental { inline namespace network_v1 { template<class Clock> struct wait_traits { static typename Clock::duration to_wait_duration( const typename Clock::duration& d); static typename Clock::duration to_wait_duration( const typename Clock::time_point& t); }; } // inline namespace network_v1 } // namespace experimental } // namespace std
Class template wait_traits
satisfies the WaitTraits
type requirements. Template argument Clock
is a type meeting the Clock
requirements (C++ Std [time.clock.req]).
static typename Clock::duration to_wait_duration( const typename Clock::duration& d);
Returns:
d
.
static typename Clock::duration to_wait_duration( const typename Clock::time_point& t);
Returns: Let
now
beClock::now()
. Ifnow + Clock::duration::max()
is beforet
,Clock::duration::max()
; ifnow + Clock::duration::min()
is aftert
,Clock::duration::min()
; otherwise,t - now
.
namespace std { namespace experimental { inline namespace network_v1 { template<class Clock, class WaitTraits = wait_traits<Clock>> class basic_waitable_timer { public: // types: typedef io_service::executor_type executor_type; typedef Clock clock_type; typedef typename clock_type::duration duration; typedef typename clock_type::time_point time_point; typedef WaitTraits traits_type; // construct / copy / destroy: explicit basic_waitable_timer(io_service& ios); basic_waitable_timer(io_service& ios, const time_point& t); basic_waitable_timer(io_service& ios, const duration& d); basic_waitable_timer(const basic_waitable_timer&) = delete; basic_waitable_timer(basic_waitable_timer&& rhs) noexcept; ~basic_waitable_timer(); basic_waitable_timer& operator=(const basic_waitable_timer&) = delete; basic_waitable_timer& operator=(basic_waitable_timer&& rhs); // basic_waitable_timer operations: executor_type get_executor() noexcept; size_t cancel(); size_t cancel_one(); time_point expiry() const; size_t expires_at(const time_point& t); size_t expires_after(const duration& d); void wait(); void wait(error_code& ec); template<class CompletionToken> DEDUCED async_wait(CompletionToken&& token); }; } // inline namespace network_v1 } // namespace experimental } // namespace std
explicit basic_waitable_timer(io_service& ios);
Effects: Equivalent to
basic_waitable_timer(ios, time_point())
.
basic_waitable_timer(io_service& ios, const time_point& t);
Postconditions:
—get_executor() == ios.get_executor()
.
—expiry() == t
.
basic_waitable_timer(io_service& ios, const duration& d);
Effects: Sets the expiry time as if by calling
expires_after(d)
.
Postconditions:
get_executor() == ios.get_executor()
.
basic_waitable_timer(basic_waitable_timer&& rhs) noexcept;
Effects: Move constructs an object of class
basic_waitable_timer<Clock, WaitTraits>
that refers to the state originally represented byrhs
.
Postconditions:
—get_executor()
is equal to the prior value ofrhs.get_executor()
.
—expiry()
returns the same value asrhs.expiry()
prior to the constructor invocation.
—rhs.expiry() == time_point()
.
~basic_waitable_timer();
Effects: Destroys the timer, cancelling any asynchronous wait operations associated with the timer as if by calling
cancel()
.
basic_waitable_timer& operator=(basic_waitable_timer&& rhs);
Effects: Cancels any outstanding asynchronous operations associated with
*this
as if by callingcancel()
, then moves into*this
the state originally represented byrhs
.
Postconditions:
—get_executor()
is equal to the prior value ofrhs.get_executor()
.
—expiry()
returns the same value asrhs.expiry()
prior to the assignment.
—rhs.expiry() == time_point()
.
Returns:
*this
.
executor_type get_executor() noexcept;
Returns: The associated executor.
size_t cancel();
Effects: Causes any outstanding asynchronous wait operations to complete. Completion handlers for canceled operations are passed an error code
ec
such thatec == errc::operation_canceled
yieldstrue
.
Returns: The number of operations that were canceled.
size_t cancel_one();
Effects: Causes the outstanding asynchronous wait operation that was initiated first, if any, to complete as soon as possible. The completion handler for the canceled operation is passed an error code
ec
such thatec == errc::operation_canceled
yieldstrue
.
Returns:
1
if an operation was cancelled, otherwise0
.
time_point expiry() const;
Returns: The expiry time associated with the timer, as previously set using
expires_at()
orexpires_after()
.
size_t expires_at(const time_point& t);
Effects: Cancels outstanding asynchronous wait operations, as if by calling
cancel()
. Sets the expiry time associated with the timer.
Returns: The number of operations that were canceled.
Postconditions:
expiry() == t
.
size_t expires_after(const duration& d);
Returns:
expires_at(clock_type::now() + d)
.
void wait(); void wait(error_code& ec);
Effects: Establishes the postcondition as if by repeatedly blocking the calling thread for the relative time produced by
WaitTraits::to_wait_duration(expiry())
.
Postconditions:
ec || !(clock_type::now() < expiry())
.
template<class CompletionToken> DEDUCED async_wait(CompletionToken&& token);
Completion signature:
void(error_code ec)
.
Effects: Initiates an asynchronous wait operation such that the completion handler is submitted for execution only when the condition
ec || !(clock_type::now() < expiry())
yieldstrue
.
namespace std { namespace experimental { inline namespace network_v1 { enum class stream_errc { eof = implementation defined, not_found = implementation defined }; const error_category& stream_category() noexcept; error_code make_error_code(stream_errc e) noexcept; error_condition make_error_condition(stream_errc e) noexcept; class mutable_buffer; class const_buffer; class mutable_buffers_1; class const_buffers_1; // buffer type traits: template<class T> is_mutable_buffer_sequence; template<class T> is_const_buffer_sequence; template<class T> is_dynamic_buffer; // buffer conversions: template<class T> T buffer_cast(const mutable_buffer& b) noexcept; template<class T> T buffer_cast(const const_buffer& b) noexcept; // buffer size: size_t buffer_size(const mutable_buffer& b) noexcept; size_t buffer_size(const const_buffer& b) noexcept; template<class ConstBufferSequence> size_t buffer_size(const ConstBufferSequence& buffers) noexcept; // buffer copy: size_t buffer_copy(const mutable_buffer& dest, const const_buffer& source) noexcept; size_t buffer_copy(const mutable_buffer& dest, const const_buffer& source, size_t max_size) noexcept; template<class ConstBufferSequence> size_t buffer_copy(const mutable_buffer& dest, const ConstBufferSequence& source) noexcept; template<class ConstBufferSequence> size_t buffer_copy(const mutable_buffer& dest, const ConstBufferSequence& source, size_t max_size) noexcept; template<class MutableBufferSequence> size_t buffer_copy(const MutableBufferSequence& dest, const const_buffer& source) noexcept; template<class MutableBufferSequence> size_t buffer_copy(const MutableBufferSequence& dest, const const_buffer& source, size_t max_size) noexcept; template<class MutableBufferSequence, class ConstBufferSequence> size_t buffer_copy(const MutableBufferSequence& dest, const ConstBufferSequence& source) noexcept; template<class MutableBufferSequence, class ConstBufferSequence> size_t buffer_copy(const MutableBufferSequence& dest, const ConstBufferSequence& source, max_size) noexcept; // buffer arithmetic: mutable_buffer operator+(const mutable_buffer& b, size_t n) noexcept; mutable_buffer operator+(size_t n, const mutable_buffer& b) noexcept; const_buffer operator+(const const_buffer&, size_t n) noexcept; const_buffer operator+(size_t, const const_buffer&) noexcept; mutable_buffers_1 operator+(const mutable_buffers_1& b, size_t n) noexcept; mutable_buffers_1 operator+(size_t n, const mutable_buffers_1& b) noexcept; const_buffers_1 operator+(const const_buffers_1&, size_t n) noexcept; const_buffers_1 operator+(size_t, const const_buffers_1&) noexcept; // buffer creation: mutable_buffers_1 buffer(void* p, size_t n) noexcept; const_buffers_1 buffer(const void* p, size_t n) noexcept; mutable_buffers_1 buffer(const mutable_buffer& b) noexcept; mutable_buffers_1 buffer(const mutable_buffer& b, size_t n) noexcept; const_buffers_1 buffer(const const_buffer& b) noexcept; const_buffers_1 buffer(const const_buffer& b, size_t n) noexcept; template<class T, size_t N> mutable_buffers_1 buffer(T (&arr)[N]) noexcept; template<class T, size_t N> mutable_buffers_1 buffer(T (&arr)[N], size_t n) noexcept; template<class T, size_t N> const_buffers_1 buffer(const T (&arr)[N]) noexcept; template<class T, size_t N> const_buffers_1 buffer(const T (&arr)[N], size_t n) noexcept; template<class T, size_t N> mutable_buffers_1 buffer(array<T, N>& arr) noexcept; template<class T, size_t N> mutable_buffers_1 buffer(array<T, N>& arr, size_t n) noexcept; template<class T, size_t N> const_buffers_1 buffer(array<const T, N>& arr) noexcept; template<class T, size_t N> const_buffers_1 buffer(array<const T, N>& arr, size_t n) noexcept; template<class T, size_t N> const_buffers_1 buffer(const array<T, N>& arr) noexcept; template<class T, size_t N> const_buffers_1 buffer(const array<T, N>& arr, size_t n) noexcept; template<class T, class Allocator> mutable_buffers_1 buffer(vector<T, Allocator>& vec) noexcept; template<class T, class Allocator> mutable_buffers_1 buffer(vector<T, Allocator>& vec, size_t n) noexcept; template<class T, class Allocator> const_buffers_1 buffer(const vector<T, Allocator>& vec) noexcept; template<class T, class Allocator> const_buffers_1 buffer(const vector<T, Allocator>& vec, size_t n) noexcept; template<class CharT, class Traits, class Allocator> mutable_buffers_1 buffer(basic_string<CharT, Traits, Allocator>& str) noexcept; template<class CharT, class Traits, class Allocator> mutable_buffers_1 buffer(basic_string<CharT, Traits, Allocator>& str, size_t n) noexcept; template<class CharT, class Traits> const_buffers_1 buffer(basic_string_view<CharT, Traits> str) noexcept; template<class CharT, class Traits> const_buffers_1 buffer(basic_string_view<CharT, Traits> str, size_t n) noexcept; template<class T, Allocator> class dynamic_vector_buffer; template<class CharT, class Traits, Allocator> class dynamic_string_buffer; // dynamic buffer creation: template<class T, class Allocator> dynamic_vector_buffer<T, Allocator> dynamic_buffer(vector<T, Allocator>& vec) noexcept; template<class T, class Allocator> dynamic_vector_buffer<T, Allocator> dynamic_buffer(vector<T, Allocator>& vec, size_t n) noexcept; template<class CharT, class Traits, class Allocator> dynamic_string_buffer<CharT, Traits, Allocator> dynamic_buffer(basic_string<CharT, Traits, Allocator>& str) noexcept; template<class CharT, class Traits, class Allocator> dynamic_string_buffer<CharT, Traits, Allocator> dynamic_buffer(basic_string<CharT, Traits, Allocator>& str, size_t n) noexcept; class transfer_all; class transfer_at_least; class transfer_exactly; // synchronous read operations: template<class SyncReadStream, class MutableBufferSequence> size_t read(SyncReadStream& stream, const MutableBufferSequence& buffers); template<class SyncReadStream, class MutableBufferSequence> size_t read(SyncReadStream& stream, const MutableBufferSequence& buffers, error_code& ec); template<class SyncReadStream, class MutableBufferSequence, class CompletionCondition> size_t read(SyncReadStream& stream, const MutableBufferSequence& buffers, CompletionCondition completion_condition); template<class SyncReadStream, class MutableBufferSequence, class CompletionCondition> size_t read(SyncReadStream& stream, const MutableBufferSequence& buffers, CompletionCondition completion_condition, error_code& ec); template<class SyncReadStream, class DynamicBuffer> size_t read(SyncReadStream& stream, DynamicBuffer&& b); template<class SyncReadStream, class DynamicBuffer> size_t read(SyncReadStream& stream, DynamicBuffer&& b, error_code& ec); template<class SyncReadStream, class DynamicBuffer, class CompletionCondition> size_t read(SyncReadStream& stream, DynamicBuffer&& b, CompletionCondition completion_condition); template<class SyncReadStream, class DynamicBuffer, class CompletionCondition> size_t read(SyncReadStream& stream, DynamicBuffer&& b, CompletionCondition completion_condition, error_code& ec); // asynchronous read operations: template<class AsyncReadStream, class MutableBufferSequence, class CompletionToken> DEDUCED async_read(AsyncReadStream& stream, const MutableBufferSequence& buffers, CompletionToken&& token); template<class AsyncReadStream, class MutableBufferSequence, class CompletionCondition, class CompletionToken> DEDUCED async_read(AsyncReadStream& stream, const MutableBufferSequence& buffers, CompletionCondition completion_condition, CompletionToken&& token); template<class AsyncReadStream, class DynamicBuffer, class CompletionToken> DEDUCED async_read(AsyncReadStream& stream, DynamicBuffer&& b, CompletionToken&& token); template<class AsyncReadStream, class DynamicBuffer, class CompletionCondition, class CompletionToken> DEDUCED async_read(AsyncReadStream& stream, DynamicBuffer&& b, CompletionCondition completion_condition, CompletionToken&& token); // synchronous write operations: template<class SyncWriteStream, class ConstBufferSequence> size_t write(SyncWriteStream& stream, const ConstBufferSequence& buffers); template<class SyncWriteStream, class ConstBufferSequence> size_t write(SyncWriteStream& stream, const ConstBufferSequence& buffers, error_code& ec); template<class SyncWriteStream, class ConstBufferSequence, class CompletionCondition> size_t write(SyncWriteStream& stream, const ConstBufferSequence& buffers, CompletionCondition completion_condition); template<class SyncWriteStream, class ConstBufferSequence, class CompletionCondition> size_t write(SyncWriteStream& stream, const ConstBufferSequence& buffers, CompletionCondition completion_condition, error_code& ec); template<class SyncWriteStream, class DynamicBuffer> size_t write(SyncWriteStream& stream, DynamicBuffer&& b); template<class SyncWriteStream, class DynamicBuffer> size_t write(SyncWriteStream& stream, DynamicBuffer&& b, error_code& ec); template<class SyncWriteStream, class DynamicBuffer, class CompletionCondition> size_t write(SyncWriteStream& stream, DynamicBuffer&& b, CompletionCondition completion_condition); template<class SyncWriteStream, class DynamicBuffer, class CompletionCondition> size_t write(SyncWriteStream& stream, DynamicBuffer&& b, CompletionCondition completion_condition, error_code& ec); // asynchronous write operations: template<class AsyncWriteStream, class ConstBufferSequence, class CompletionToken> DEDUCED async_write(AsyncWriteStream& stream, const ConstBufferSequence& buffers, CompletionToken&& token); template<class AsyncWriteStream, class ConstBufferSequence, class CompletionCondition, class CompletionToken> DEDUCED async_write(AsyncWriteStream& stream, const ConstBufferSequence& buffers, CompletionCondition completion_condition, CompletionToken&& token); template<class AsyncWriteStream, class DynamicBuffer, class CompletionToken> DEDUCED async_write(AsyncWriteStream& stream, DynamicBuffer&& b, CompletionToken&& token); template<class AsyncWriteStream, class DynamicBuffer, class CompletionCondition, class CompletionToken> DEDUCED async_write(AsyncWriteStream& stream, DynamicBuffer&& b, CompletionCondition completion_condition, CompletionToken&& token); // synchronous delimited read operations: template<class SyncReadStream, class DynamicBuffer> size_t read_until(SyncReadStream& s, DynamicBuffer&& b, char delim); template<class SyncReadStream, class DynamicBuffer> size_t read_until(SyncReadStream& s, DynamicBuffer&& b, char delim, error_code& ec); template<class SyncReadStream, class DynamicBuffer> size_t read_until(SyncReadStream& s, DynamicBuffer&& b, string_view delim); template<class SyncReadStream, class DynamicBuffer> size_t read_until(SyncReadStream& s, DynamicBuffer&& b, string_view delim, error_code& ec); // asynchronous delimited read operations: template<class AsyncReadStream, class DynamicBuffer, class CompletionToken> DEDUCED async_read_until(AsyncReadStream& s, DynamicBuffer&& b, char delim, CompletionToken&& token); template<class AsyncReadStream, class DynamicBuffer, class CompletionToken> DEDUCED async_read_until(AsyncReadStream& s, DynamicBuffer&& b, string_view delim, CompletionToken&& token); } // inline namespace network_v1 } // namespace experimental template<> struct is_error_code_enum< experimental::network_v1::stream_errc> : public true_type {}; } // namespace std
[buffer.reqmts.mutablebuffersequence]
A mutable buffer sequence satisfies the requirements for a constant buffer sequence as well as the requirements in the table below.
In the table below, X
denotes a constant buffer sequence class, x
denotes a (possibly const) value of type X
,
and u
denotes an identifier.
Table 10. ConstBufferSequence requirements
expression |
return type |
assertion/note |
---|---|---|
|
convertible to | |
X u(x);
|
post: equal(u.begin(), u.end(), x.begin(), x.end(), [](const typename X::value_type& v1, const typename X::value_type& v2) { mutable_buffer b1(v1); mutable_buffer b2(v2); return buffer_cast<void*>(b1) == buffer_cast<void*>(b2) && buffer_size(b1) == buffer_size(b2); })
|
[buffer.reqmts.constbuffersequence]
A constant buffer sequence satisfies the requirements
for Destructible
(C++
Std, [destructible]) and CopyConstructible
(C++ Std, [copyconstructible]), as well as the requirements in the table
below.
In the table below, X
denotes a constant buffer sequence class, x
denotes a (possibly const) value of type X
,
and u
denotes an identifier.
Table 11. ConstBufferSequence requirements
expression |
return type |
assertion/note |
---|---|---|
|
convertible to | |
|
iterator type pointing to |
|
X u(x);
|
post: equal(u.begin(), u.end(), x.begin(), x.end(), [](const typename X::value_type& v1, const typename X::value_type& v2) { const_buffer b1(v1); const_buffer b2(v2); return buffer_cast<const void*>(b1) == buffer_cast<const void*>(b2) && buffer_size(b1) == buffer_size(b2); })
| |
|
convertible to | |
|
convertible to |
A dynamic buffer encapsulates memory storage that may be automatically
resized as required, where the memory is divided into an input sequence
followed by an output sequence. These memory regions are internal to
the dynamic buffer, but direct access to the elements is provided to
permit them to be efficiently used with I/O operations, such as the
send
or receive
operations of a socket. Data
written to the output sequence of a dynamic buffer object is appended
to the input sequence of the same object.
A dynamic buffer type X
shall satisfy the requirements of MoveConstructible
(C++ Std, [moveconstructible]) types in addition to those listed below.
In the table below, X
denotes a dynamic buffer class, x
denotes a value of type X
,
x1
denotes a (possibly
const) value of type X
,
and n
denotes a value
of type size_t
.
Table 12. DynamicBuffer requirements
expression |
type |
assertion/note |
---|---|---|
|
type meeting ConstBufferSequence requirements. |
This type represents the memory associated with the input sequence. |
|
type meeting MutableBufferSequence requirements. |
This type represents the memory associated with the output sequence. |
|
|
Returns the size, in bytes, of the input sequence. |
|
|
Returns the permitted maximum of the sum of the sizes of the input sequence and output sequence. |
|
|
Returns the maximum sum of the sizes of the input sequence and output sequence that the dynamic buffer can hold without requiring reallocation. |
|
|
Returns a constant buffer sequence |
|
|
Returns a mutable buffer sequence |
|
Appends | |
|
Removes |
In this Technical Specification, a synchronous read operation is a function that reads data into a mutable buffer sequence argument of a type meeting MutableBufferSequence requirements.
The mutable buffer sequence specifies memory where the data should be placed. A synchronous read operation shall always fill a buffer in the sequence completely before proceeding to the next.
In this Technical Specification, an asynchronous read operation is an asynchronous operation that reads data into a mutable buffer sequence argument of a type meeting MutableBufferSequence requirements.
The mutable buffer sequence specifies memory where the data should be placed. An asynchronous read operation shall always fill a buffer in the sequence completely before proceeding to the next.
The read operation's implementation shall maintain one or more copies of the buffer sequence until such time as the read operation no longer requires access to the memory specified by the buffers in the sequence. The program shall ensure the memory remains valid until:
— the last copy of the buffer sequence is destroyed, or
— the completion handler for the asynchronous operation is invoked,
whichever comes first.
In this Technical Specification, a synchronous write operation is a function that writes data from a constant buffer sequence argument of a type meeting ConstBufferSequence requirements.
The constant buffer sequence specifies memory where the data to be written is located. A synchronous write operation shall always write a buffer in the sequence completely before proceeding to the next.
In this Technical Specification, an asynchronous write operation is an asynchronous operation that writes data from a constant buffer sequence argument of a type meeting ConstBufferSequence requirements.
The constant buffer sequence specifies memory where the data to be written is located. An asynchronous write operation shall always write a buffer in the sequence completely before proceeding to the next.
The write operation's implementation shall maintain one or more copies of the buffer sequence until such time as the write operation no longer requires access to the memory specified by the buffers in the sequence. The program shall ensure the memory remains valid until:
— the last copy of the buffer sequence is destroyed, or
— the completion handler for the asynchronous operation is invoked,
whichever comes first.
[buffer.reqmts.syncreadstream]
In the table below, a
denotes a synchronous read stream object, mb
denotes an object satisfying mutable
buffer sequence requirements, and ec
denotes an object of type error_code
.
Table 13. Buffer-oriented synchronous read stream requirements
operation |
type |
semantics, pre/post-conditions |
---|---|---|
|
|
Meets the requirements for a synchronous
read operation. |
[buffer.reqmts.asyncreadstream]
In the table below, a
denotes an asynchronous read stream object, mb
denotes an object satisfying mutable
buffer sequence requirements, and t
is a completion token.
Table 14. Buffer-oriented asynchronous read stream requirements
operation |
type |
semantics, pre/post-conditions |
---|---|---|
|
A type satisfying the Executor requirements. |
Returns the associated I/O executor. |
|
The return type is determined according to the requirements for an asynchronous operation. |
Meets the requirements for an asynchronous
read operation with completion signature |
[buffer.reqmts.syncwritestream]
In the table below, a
denotes a synchronous write stream object, cb
denotes an object satisfying constant
buffer sequence requirements, and ec
denotes an object of type error_code
.
Table 15. Buffer-oriented synchronous write stream requirements
operation |
type |
semantics, pre/post-conditions |
---|---|---|
|
|
Meets the requirements for a synchronous
write operation. |
[buffer.reqmts.asyncwritestream]
In the table below, a
denotes an asynchronous write stream object, cb
denotes an object satisfying constant
buffer sequence requirements, and t
is a completion token.
Table 16. Buffer-oriented asynchronous write stream requirements
operation |
type |
semantics, pre/post-conditions |
---|---|---|
|
A type satisfying the Executor requirements. |
Returns the associated I/O executor. |
|
The return type is determined according to the requirements for an asynchronous operation. |
Meets the requirements for an asynchronous
write operation with completion signature |
namespace std { namespace experimental { inline namespace network_v1 { class mutable_buffer { public: // constructors: mutable_buffer() noexcept; mutable_buffer(void* p, size_t n) noexcept; private: void* data_; // exposition only size_t size_; // exposition only }; } // inline namespace network_v1 } // namespace experimental } // namespace std
mutable_buffer() noexcept;
Postconditions:
data_ == nullptr
andsize_ == 0
.
mutable_buffer(void* p, size_t n) noexcept;
Postconditions:
data_ == p
andsize_ == n
.
namespace std { namespace experimental { inline namespace network_v1 { class const_buffer { public: // constructors: const_buffer() noexcept; const_buffer(const void* p, size_t n) noexcept; const_buffer(const mutable_buffer& b) noexcept; private: const void* data_; // exposition only size_t size_; // exposition only }; } // inline namespace network_v1 } // namespace experimental } // namespace std
const_buffer() noexcept;
Postconditions:
data_ == nullptr
andsize_t == 0
.
const_buffer(const void* p, size_t n) noexcept;
Postconditions:
data_ == p
andsize_ == n
.
const_buffer(const mutable_buffer& b);
Postconditions:
data_ == b.data_
andsize_ == b.size_
.
An object of class mutable_buffers_1
represents a sequence of exactly one mutable_buffer
object.
namespace std { namespace experimental { inline namespace network_v1 { class mutable_buffers_1 : public mutable_buffer { public: // types: typedef mutable_buffer value_type; typedef unspecified const_iterator; // constructors: mutable_buffers_1(void* p, size_t n) noexcept; explicit mutable_buffers_1(const mutable_buffer& b) noexcept; // members: const_iterator begin() const noexcept; const_iterator end() const noexcept; }; } // inline namespace network_v1 } // namespace experimental } // namespace std
The mutable_buffers_1
class
meets the requirements for MutableBufferSequence
.
mutable_buffers_1(const void* p, size_t n) noexcept;
Effects: Constructs an object of class
mutable_buffers_1
, initializing the base class withmutable_buffer(p, n)
.
explicit mutable_buffers_1(const mutable_buffer& b) noexcept;
Effects: Constructs an object of class
mutable_buffers_1
, initializing the base class withmutable_buffer(b)
.
const_iterator begin() const noexcept;
Returns: An iterator referring to the first (and only)
mutable_buffer
object in the sequence.
const_iterator end() const noexcept;
Returns: An iterator which is the past-the-end value.
An object of class const_buffers_1
represents a sequence of exactly one const_buffer
object.
namespace std { namespace experimental { inline namespace network_v1 { class const_buffers_1 : public const_buffer { public: // types: typedef const_buffer value_type; typedef unspecified const_iterator; // constructors: const_buffers_1(const void* p, size_t n) noexcept; explicit const_buffers_1(const const_buffer& b) noexcept; // members: const_iterator begin() const noexcept; const_iterator end() const noexcept; }; } // inline namespace network_v1 } // namespace experimental } // namespace std
The const_buffers_1
class
meets the requirements for ConstBufferSequence
.
const_buffers_1(const void* p, size_t n) noexcept;
Effects: Constructs an object of class
const_buffers_1
, initializing the base class withconst_buffer(p, n)
.
explicit const_buffers_1(const const_buffer& b) noexcept;
Effects: Constructs an object of class
const_buffers_1
, initializing the base class withconst_buffer(b)
.
const_iterator begin() const noexcept;
Returns: An iterator referring to the first (and only)
const_buffer
object in the sequence.
const_iterator end() const noexcept;
Returns: An iterator which is the past-the-end value.
namespace std { namespace experimental { inline namespace network_v1 { template<class T> is_mutable_buffer_sequence; template<class T> is_const_buffer_sequence; template<class T> is_dynamic_buffer; } // inline namespace network_v1 } // namespace experimental } // namespace std
This sub-clause contains templates that may be used to query the properties
of a type at compile time. Each of these templates shall be a UnaryTypeTrait
(C++ Std, [meta.rqmts]) with a BaseCharacteristic of true_type
if the corresponding condition is true, otherwise false_type
.
Table 17. Buffer type traits
Template |
Condition |
Preconditions |
---|---|---|
|
|
|
|
|
|
|
|
|
template<class T> T buffer_cast(const mutable_buffer& b) noexcept; template<class T> T buffer_cast(const const_buffer& b) noexcept;
Returns:
static_cast<T>(b.data_)
.
size_t buffer_size(const mutable_buffer& b) noexcept; size_t buffer_size(const const_buffer& b) noexcept;
Returns:
b.size_
.
template<class ConstBufferSequence> size_t buffer_size(const ConstBufferSequence& buffers) noexcept;
Returns: The total size of all buffers in the sequence, as if computed as follows:
size_t total_size = 0; for (const auto& v: buffers) { const_buffer b(v); total_size += b.size_; } return total_size;
Remarks: This function overload shall not participate in overload resolution unless
is_const_buffer_sequence<ConstBufferSequence>::value
istrue
.
size_t buffer_copy(const mutable_buffer& dest, const const_buffer& source) noexcept; size_t buffer_copy(const mutable_buffer& dest, const const_buffer& source, size_t max_size) noexcept; template<class ConstBufferSequence> size_t buffer_copy(const mutable_buffer& dest, const ConstBufferSequence& source) noexcept; template<class ConstBufferSequence> size_t buffer_copy(const mutable_buffer& dest, const ConstBufferSequence& source, size_t max_size) noexcept; template<class MutableBufferSequence> size_t buffer_copy(const MutableBufferSequence& dest, const const_buffer& source) noexcept; template<class MutableBufferSequence> size_t buffer_copy(const MutableBufferSequence& dest, const const_buffer& source, size_t max_size) noexcept; template<class MutableBufferSequence, class ConstBufferSequence> size_t buffer_copy(const MutableBufferSequence& dest, const ConstBufferSequence& source) noexcept; template<class MutableBufferSequence, class ConstBufferSequence> size_t buffer_copy(const MutableBufferSequence& dest, const ConstBufferSequence& source, size_t max_size) noexcept;
Effects: Copies bytes from the buffer or buffer sequence
source
to the buffer or buffer sequencedest
, as if by calls tomemcpy
.
The number of bytes copied is the lesser of:
—buffer_size(dest)
;
—buffer_size(source)
; and
—max_size
, if specified.
The mutable buffer or mutable buffer sequence
dest
specifies memory where the data should be placed. The operation always fills a buffer in the sequence completely before proceeding to the next.
The constant buffer or constant buffer sequence
source
specifies memory where the data to be written is located. The operation always copies a buffer in the sequence completely before proceeding to the next.
Returns: The number of bytes copied from
source
todest
.
Remarks: Where an overload accepts a template parameter
MutableBufferSequence
, the overload shall not participate in overload resolution unlessis_mutable_buffer_sequence<MutableBufferSequence>::value
istrue
. Where an overload accepts a template parameterConstBufferSequence
, the overload shall not participate in overload resolution unlessis_const_buffer_sequence<ConstBufferSequence>::value
istrue
.
mutable_buffer operator+(const mutable_buffer& b, size_t n) noexcept; mutable_buffer operator+(size_t n, const mutable_buffer& b) noexcept;
Returns: A
mutable_buffer
equivalent tomutable_buffer( buffer_cast<char*>(b) + min(n, buffer_size(b)), buffer_size(b) - min(n, buffer_size(b)));
const_buffer operator+(const const_buffer& b, size_t n) noexcept; const_buffer operator+(size_t n, const const_buffer& b) noexcept;
Returns: A
const_buffer
equivalent toconst_buffer( buffer_cast<const char*>(b) + min(n, buffer_size(b)), buffer_size(b) - min(n, buffer_size(b)));
mutable_buffers_1 operator+(const mutable_buffers_1& b, size_t n) noexcept; mutable_buffers_1 operator+(size_t n, const mutable_buffers_1& b) noexcept;
Returns: A
mutable_buffers_1
equivalent tomutable_buffers_1( buffer_cast<char*>(b) + min(n, buffer_size(b)), buffer_size(b) - min(n, buffer_size(b)));
const_buffers_1 operator+(const const_buffers_1& b, size_t n) noexcept; const_buffers_1 operator+(size_t n, const const_buffers_1& b) noexcept;
Returns: A
const_buffers_1
equivalent toconst_buffers_1( buffer_cast<const char*>(b) + min(n, buffer_size(b)), buffer_size(b) - min(n, buffer_size(b)));
In the functions below, T
must be a trivially copyable type.
For the function overloads below that accept an argument of type vector<>
,
the buffer objects returned are invalidated by any vector operation that
also invalidates all references, pointers and iterators referring to the
elements in the sequence (C++ Std, [vector]).
For the function overloads below that accept an argument of type basic_string<>
,
the buffer objects returned are invalidated according to the rules defined
for invalidation of references, pointers and iterators referring to elements
of the sequence (C++ Std, [string.require]).
mutable_buffers_1 buffer(void* p, size_t n) noexcept;
Returns:
mutable_buffers_1(p, n)
.
const_buffers_1 buffer(const void* p, size_t n) noexcept;
Returns:
const_buffers_1(p, n)
.
mutable_buffers_1 buffer(const mutable_buffer& b) noexcept;
Returns:
mutable_buffers_1(b)
.
mutable_buffers_1 buffer(const mutable_buffer& b, size_t n) noexcept;
Returns: A
mutable_buffers_1
value equivalent to:mutable_buffers_1( buffer_cast<void*>(b), min(buffer_size(b), n));
const_buffers_1 buffer(const const_buffer& b) noexcept;
Returns:
const_buffers_1(b)
.
const_buffers_1 buffer(const const_buffer& b, size_t n) noexcept;
Returns: A
const_buffers_1
value equivalent to:const_buffers_1( buffer_cast<const void*>(b), min(buffer_size(b), n));
template<class T, size_t N> mutable_buffers_1 buffer(T (&arr)[N]) noexcept;
Returns: A
mutable_buffers_1
value equivalent to:mutable_buffers_1( static_cast<void*>(arr), N * sizeof(T));
template<class T, size_t N> mutable_buffers_1 buffer(T (&arr)[N], size_t n) noexcept;
Returns: A
mutable_buffers_1
value equivalent to:mutable_buffers_1( static_cast<void*>(arr), min(N * sizeof(T), n));
template<class T, size_t N> const_buffers_1 buffer(const T (&arr)[N]) noexcept;
Returns: A
const_buffers_1
value equivalent to:const_buffers_1( static_cast<const void*>(arr), N * sizeof(T));
template<class T, size_t N> const_buffers_1 buffer(const T (&arr)[N], size_t n) noexcept;
Returns: A
const_buffers_1
value equivalent to:const_buffers_1( static_cast<const void*>(arr), min(N * sizeof(T), n));
template<class T, size_t N> mutable_buffers_1 buffer(array<T, N>& arr) noexcept;
Returns: A
mutable_buffers_1
value equivalent to:mutable_buffers_1( arr.data(), arr.size() * sizeof(T));
template<class T, size_t N> mutable_buffers_1 buffer(array<T, N>& arr, size_t n) noexcept;
Returns: A
mutable_buffers_1
value equivalent to:mutable_buffers_1( arr.data(), min(arr.size() * sizeof(T), n));
template<class T, size_t N> const_buffers_1 buffer(array<const T, N>& arr) noexcept;
Returns: A
const_buffers_1
value equivalent to:const_buffers_1( arr.data(), arr.size() * sizeof(T));
template<class T, size_t N> const_buffers_1 buffer(array<const T, N>& arr, size_t n) noexcept;
Returns: A
const_buffers_1
value equivalent to:const_buffers_1( arr.data(), min(arr.size() * sizeof(T), n));
template<class T, size_t N> const_buffers_1 buffer(const array<T, N>& arr) noexcept;
Returns: A
const_buffers_1
value equivalent to:const_buffers_1( arr.data(), arr.size() * sizeof(T));
template<class T, size_t N> const_buffers_1 buffer(const array<T, N>& arr, size_t n) noexcept;
Returns: A
const_buffers_1
value equivalent to:const_buffers_1( arr.data(), min(arr.size() * sizeof(T), n));
template<class T, class Allocator> mutable_buffers_1 buffer(vector<T, Allocator>& vec) noexcept;
Returns: A
mutable_buffers_1
value equivalent to:mutable_buffers_1( !vec.empty() ? &vec[0] : nullptr, vec.size() * sizeof(T));
template<class T, class Allocator> mutable_buffers_1 buffer(vector<T, Allocator>& vec, size_t n) noexcept;
Returns: A
mutable_buffers_1
value equivalent to:mutable_buffers_1( !vec.empty() ? &vec[0] : nullptr, min(vec.size() * sizeof(T), n));
template<class T, class Allocator> const_buffers_1 buffer(const vector<T, Allocator>& vec) noexcept;
Returns: A
const_buffers_1
value equivalent to:const_buffers_1( !vec.empty() ? &vec[0] : nullptr, vec.size() * sizeof(T));
template<class T, class Allocator> const_buffers_1 buffer(const vector<T, Allocator>& vec, size_t n) noexcept;
Returns: A
const_buffers_1
value equivalent to:const_buffers_1( !vec.empty() ? &vec[0] : nullptr, min(vec.size() * sizeof(T), n));
template<class CharT, class Traits, class Allocator> mutable_buffers_1 buffer(basic_string<CharT, Traits, Allocator>& str) noexcept;
Returns: A
mutable_buffers_1
value equivalent to:mutable_buffers_1( !str.empty() ? &str[0] : nullptr, str.size() * sizeof(CharT));
template<class CharT, class Traits, class Allocator> mutable_buffers_1 buffer(basic_string<CharT, Traits, Allocator>& str, size_t n) noexcept;
Returns: A
mutable_buffers_1
value equivalent to:mutable_buffers_1( !str.empty() ? &str[0] : nullptr, min(str.size() * sizeof(CharT), n));
template<class CharT, class Traits> const_buffers_1 buffer(basic_string_view<CharT, Traits> str) noexcept;
Returns: A
const_buffers_1
value equivalent to:const_buffers_1( str.data(), str.size() * sizeof(CharT));
template<class CharT, class Traits> const_buffers_1 buffer(basic_string_view<CharT, Traits> str, size_t n) noexcept;
Returns: A
const_buffers_1
value equivalent to:const_buffers_1( str.data(), min(str.size() * sizeof(CharT), n));
The dynamic_vector_buffer
class template meets the requirements for DynamicBuffer
.
namespace std { namespace experimental { inline namespace network_v1 { template<class T, class Allocator> class dynamic_vector_buffer { public: // types: typedef const_buffers_1 const_buffers_type; typedef mutable_buffers_1 mutable_buffers_type; // constructors: explicit dynamic_vector_buffer(vector<T, Allocator>& vec) noexcept; dynamic_vector_buffer(vector<T, Allocator>& vec, size_t maximum_size) noexcept; dynamic_vector_buffer(dynamic_vector_buffer&&) = default; // members: size_t size() const noexcept; size_t max_size() const noexcept; size_t capacity() const noexcept; const_buffers_type data() const noexcept; mutable_buffers_type prepare(size_t n); void commit(size_t n); void consume(size_t n); private: vector<T, Allocator>& vec_; // exposition only size_t size_; // exposition only const size_t max_size_; // exposition only }; } // inline namespace network_v1 } // namespace experimental } // namespace std
The dynamic_vector_buffer
class template requires that T
is a trivially copyable type and that sizeof(T)
== 1
.
explicit dynamic_vector_buffer(vector<T, Allocator>& vec) noexcept;
Effects: Initializes
vec_
withvec
,size_
withvec.size()
, andmax_size_
withvec.max_size()
.
dynamic_vector_buffer(vector<T, Allocator>& vec, size_t maximum_size) noexcept;
Requires:
vec.size() <= maximum_size
.
Effects: Initializes
vec_
withvec
,size_
withvec.size()
, andmax_size_
withmaximum_size
.
[buffer.dynamic.vector.members]
size_t size() const noexcept;
Returns:
size_
.
size_t max_size() const noexcept;
Returns:
max_size_
.
size_t capacity() const noexcept;
Returns:
vec_.capacity()
.
const_buffers_type data() const noexcept;
Returns:
buffer(vec_, size_)
.
mutable_buffers_type prepare(size_t n);
Effects: Performs
vec_.resize(size_ + n)
.
Returns:
buffer(buffer(vec_) + size_, n)
.
Throws:
length_error
ifsize() + n
exceedsmax_size()
.
void commit(size_t n);
Effects: Performs:
size_ += min(n, vec_.size() - size_); vec_.resize(size_);
void consume(size_t n);
Effects: Performs:
size_t m = min(n, size_); vec_.erase(vec_.begin(), vec_.begin() + m); size_ -= m;
The dynamic_string_buffer
class template meets the requirements for DynamicBuffer
.
namespace std { namespace experimental { inline namespace network_v1 { template<class CharT, class Traits, class Allocator> class dynamic_string_buffer { public: // types: typedef const_buffers_1 const_buffers_type; typedef mutable_buffers_1 mutable_buffers_type; // constructors: explicit dynamic_string_buffer(basic_string<CharT, Traits, Allocator>& str) noexcept; dynamic_string_buffer(basic_string<CharT, Traits, Allocator>& str, size_t maximum_size) noexcept; dynamic_string_buffer(dynamic_string_buffer&&) = default; // members: size_t size() const noexcept; size_t max_size() const noexcept; size_t capacity() const noexcept; const_buffers_type data() const noexcept; mutable_buffers_type prepare(size_t n); void commit(size_t n) noexcept; void consume(size_t n); private: basic_string<CharT, Traits, Allocator>& str_; // exposition only size_t size_; // exposition only const size_t max_size_; // exposition only }; } // inline namespace network_v1 } // namespace experimental } // namespace std
The dynamic_string_buffer
class template requires that sizeof(CharT) == 1
.
explicit dynamic_string_buffer(basic_string<CharT, Traits, Allocator>& str) noexcept;
Effects: Initializes
str_
withstr
,size_
withstr.size()
, andmax_size_
withstr.max_size()
.
dynamic_string_buffer(basic_string<CharT, Traits, Allocator>& str, size_t maximum_size) noexcept;
Requires:
str.size() <= maximum_size
.
Effects: Initializes
str_
withstr
,size_
withstr.size()
, andmax_size_
withmaximum_size
.
[buffer.dynamic.string.members]
size_t size() const noexcept;
Returns:
size_
.
size_t max_size() const noexcept;
Returns:
max_size_
.
size_t capacity() const noexcept;
Returns:
str_.capacity()
.
const_buffers_type data() const noexcept;
Returns:
buffer(str_, size_)
.
mutable_buffers_type prepare(size_t n);
Effects: Performs
str_.resize(size_ + n)
.
Returns:
buffer(buffer(str_) + size_, n)
.
Throws:
length_error
ifsize() + n
exceedsmax_size()
.
void commit(size_t n) noexcept;
Effects: Performs:
size_ += min(n, str_.size() - size_); str_.resize(size_);
void consume(size_t n);
Effects: Performs:
size_t m = min(n, size_); str_.erase(m); size_ -= m;
template<class T, class Allocator> dynamic_vector_buffer<T, Allocator> dynamic_buffer(vector<T, Allocator>& vec) noexcept;
Returns:
dynamic_vector_buffer<T, Allocator>(vec)
.
template<class T, class Allocator> dynamic_vector_buffer<T, Allocator> dynamic_buffer(vector<T, Allocator>& vec, size_t n) noexcept;
Returns:
dynamic_vector_buffer<T, Allocator>(vec, n)
.
template<class CharT, class Traits, class Allocator> dynamic_string_buffer<CharT, Traits, Allocator> dynamic_buffer(basic_string<CharT, Traits, Allocator>& str) noexcept;
Returns:
dynamic_string_buffer<CharT, Traits, Allocator>(str)
.
template<class CharT, class Traits, class Allocator> dynamic_string_buffer<CharT, Traits, Allocator> dynamic_buffer(basic_string<CharT, Traits, Allocator>& str, size_t n) noexcept;
Returns:
dynamic_string_buffer<CharT, Traits, Allocator>(str, n)
.
namespace std { namespace experimental { inline namespace network_v1 { class transfer_all { public: size_t operator()(const error_code& ec, size_t) const; }; } // inline namespace network_v1 } // namespace experimental } // namespace std
The class transfer_all
is a function object type for use as a CompletionCondition
argument to synchronous read, asynchronous read, synchronous
write, or asynchronous write
operations.
size_t operator()(const error_code& ec, size_t) const;
Returns: If
!ec
, an unspecified non-zero value. Otherwise0
.
namespace std { namespace experimental { inline namespace network_v1 { class transfer_at_least { public: explicit transfer_at_least(size_t m); size_t operator()(const error_code& ec, size_t s) const; private: size_t minimum_; // exposition only }; } // inline namespace network_v1 } // namespace experimental } // namespace std
The class transfer_at_least
is a function object type for use as a CompletionCondition
argument to synchronous read, asynchronous read, synchronous
write, or asynchronous write
operations.
explicit transfer_at_least(size_t m);
Postconditions:
minimum_ == m
.
size_t operator()(const error_code& ec, size_t n) const;
Returns: If
!ec && n < minimum_
, an unspecified non-zero value. Otherwise0
.
namespace std { namespace experimental { inline namespace network_v1 { class transfer_exactly { public: explicit transfer_exactly(size_t e); size_t operator()(const error_code& ec, size_t s) const; private: size_t exact_; // exposition only }; } // inline namespace network_v1 } // namespace experimental } // namespace std
The class transfer_exactly
is a function object type for use as a CompletionCondition
argument to synchronous read, asynchronous read, synchronous
write, or asynchronous write
operations.
explicit transfer_exactly(size_t e);
Postconditions:
exact_ == e
.
size_t operator()(const error_code& ec, size_t n) const;
Returns: If
!ec && n < exact_
, the result ofmin(exact_ - n, N)
, whereN
is an unspecified non-zero value. Otherwise0
.
template<class SyncReadStream, class MutableBufferSequence> size_t read(SyncReadStream& stream, const MutableBufferSequence& buffers); template<class SyncReadStream, class MutableBufferSequence> size_t read(SyncReadStream& stream, const MutableBufferSequence& buffers, error_code& ec); template<class SyncReadStream, class MutableBufferSequence, class CompletionCondition> size_t read(SyncReadStream& stream, const MutableBufferSequence& buffers, CompletionCondition completion_condition); template<class SyncReadStream, class MutableBufferSequence, class CompletionCondition> size_t read(SyncReadStream& stream, const MutableBufferSequence& buffers, CompletionCondition completion_condition, error_code& ec);
Effects: Clears
ec
, then reads data from the buffer-oriented synchronous read stream objectstream
by performing zero or more calls to the stream'sread_some
member function.
The
completion_condition
parameter specifies a function object to be called prior to each call to the stream'sread_some
member function. The function object is passed theerror_code
value from the most recentread_some
call, and the total number of bytes transferred in the synchronous read operation so far. The function object return value specifies the maximum number of bytes to be read on the subsequentread_some
call. Overloads where a completion condition is not specified behave as if called with an object of classtransfer_all
.
The synchronous read operation continues until:
— the total number of bytes transferred is equal to
buffer_size(buffers)
; or
— the completion condition returns
0
.
On return,
ec
contains theerror_code
value from the most recentread_some
call.
Returns: The total number of bytes transferred in the synchronous read operation.
Remarks: This function shall not participate in overload resolution unless
is_mutable_buffer_sequence<MutableBufferSequence>::value
istrue
.
template<class SyncReadStream, class DynamicBuffer> size_t read(SyncReadStream& stream, DynamicBuffer&& b); template<class SyncReadStream, class DynamicBuffer> size_t read(SyncReadStream& stream, DynamicBuffer&& b, error_code& ec); template<class SyncReadStream, class DynamicBuffer, class CompletionCondition> size_t read(SyncReadStream& stream, DynamicBuffer&& b, CompletionCondition completion_condition); template<class SyncReadStream, class DynamicBuffer, class CompletionCondition> size_t read(SyncReadStream& stream, DynamicBuffer&& b, CompletionCondition completion_condition, error_code& ec);
Effects: Clears
ec
, then reads data from the synchronous read stream objectstream
by performing zero or more calls to the stream'sread_some
member function.
Data is placed into the dynamic buffer object
b
. A mutable buffer sequence is obtained prior to eachread_some
call usingb.prepare(N)
, whereN
is an unspecified value less than or equal tob.max_size() - b.size()
. [Note: Implementations are encouraged to useb.capacity()
when determiningN
, to minimize the number ofread_some
calls performed on the stream. —end note] After eachread_some
call, the implementation performsb.commit(n)
, wheren
is the return value fromread_some
.
The
completion_condition
parameter specifies a function object to be called prior to each call to the stream'sread_some
member function. The function object is passed theerror_code
value from the most recentread_some
call, and the total number of bytes transferred in the synchronous read operation so far. The function object return value specifies the maximum number of bytes to be read on the subsequentread_some
call. Overloads where a completion condition is not specified behave as if called with an object of classtransfer_all
.
The synchronous read operation continues until:
—
b.size() == b.max_size()
; or
— the completion condition returns
0
.
On return,
ec
contains theerror_code
value from the most recentread_some
call.
Returns: The total number of bytes transferred in the synchronous read operation.
Remarks: This function shall not participate in overload resolution unless
is_dynamic_buffer<DynamicBuffer>::value
istrue
.
template<class AsyncReadStream, class MutableBufferSequence, class CompletionToken> DEDUCED async_read(AsyncReadStream& stream, const MutableBufferSequence& buffers, CompletionToken&& token); template<class AsyncReadStream, class MutableBufferSequence, class CompletionCondition, class CompletionToken> DEDUCED async_read(AsyncReadStream& stream, const MutableBufferSequence& buffers, CompletionCondition completion_condition, CompletionToken&& token);
Completion signature:
void(error_code ec, size_t n)
.
Effects: Reads data from the buffer-oriented asynchronous read stream object
stream
by invoking the stream'sasync_read_some
member function (henceforth referred to as asynchronous read_some operations) zero or more times.
The
completion_condition
parameter specifies a function object to be called prior to each asynchronous read_some operation. The function object is passed theerror_code
value from the most recent asynchronous read_some operation, and the total number of bytes transferred in the asynchronous read operation so far. The function object return value specifies the maximum number of bytes to be read on the subsequent asynchronous read_some operation. Overloads where a completion condition is not specified behave as if called with an object of classtransfer_all
.
This asynchronous read operation is outstanding until:
— the total number of bytes transferred is equal to
buffer_size(buffers)
; or
— the completion condition returns
0
.
The program shall ensure the
AsyncReadStream
objectstream
is valid until the handler for the asynchronous operation is invoked.
On completion of the asynchronous operation,
ec
is theerror_code
value from the most recent asynchronous read_some operation, andn
is the total number of bytes transferred.
Remarks: This function shall not participate in overload resolution unless
is_mutable_buffer_sequence<MutableBufferSequence>::value
istrue
.
template<class AsyncReadStream, class DynamicBuffer, class CompletionToken> DEDUCED async_read(AsyncReadStream& stream, DynamicBuffer&& b, CompletionToken&& token); template<class AsyncReadStream, class DynamicBuffer, class CompletionCondition, class CompletionToken> DEDUCED async_read(AsyncReadStream& stream, DynamicBuffer&& b, CompletionCondition completion_condition, CompletionToken&& token);
Completion signature:
void(error_code ec, size_t n)
.
Effects: Initiates an asynchronous operation to read data from the buffer-oriented asynchronous read stream object
stream
by performing one or more asynchronous read_some operations on the stream.
Data is placed into the dynamic buffer object
b
. A mutable buffer sequence is obtained prior to eachasync_read_some
call usingb.prepare(N)
, whereN
is an unspecified value such thatN
is less than or equal tob.max_size() - b.size()
. [Note: Implementations are encouraged to useb.capacity()
when determiningN
, to minimize the number of asynchronous read_some operations performed on the stream. —end note] After the completion of each asynchronous read_some operation, the implementation performsb.commit(n)
, wheren
is the value passed to the asynchronous read_some operation's completion handler.
The
completion_condition
parameter specifies a function object to be called prior to each asynchronous read_some operation. The function object is passed theerror_code
value from the most recent asynchronous read_some operation, and the total number of bytes transferred in the asynchronous read operation so far. The function object return value specifies the maximum number of bytes to be read on the subsequent asynchronous read_some operation. Overloads where a completion condition is not specified behave as if called with an object of classtransfer_all
.
The asynchronous read operation is outstanding until:
—
b.size() == b.max_size()
; or
— the completion condition returns
0
.
The program must ensure both the
AsyncReadStream
objectstream
and the memory associated with the dynamic bufferb
are valid until the handler for the asynchronous operation is invoked.
On completion of the asynchronous operation,
ec
is theerror_code
value from the most recent asynchronous read_some operation, andn
is the total number of bytes transferred.
Remarks: This function shall not participate in overload resolution unless
is_dynamic_buffer<DynamicBuffer>::value
istrue
.
template<class SyncWriteStream, class ConstBufferSequence> size_t write(SyncWrite