Document number: P1322R3 Date: 2021-02-05 Project: Programming Language C++ Audience: SG4 - Networking, LEWG Reply-to: Christopher Kohlhoff <chris@kohlhoff.com>
At present, the Networking TS's I/O objects -- sockets, acceptors, resolvers, and timers -- can be associated only with concrete execution contexts of type io_context
. This paper proposes a minor change to the specification to permit the construction of these objects with arbitrary I/O executors and execution contexts. This would:
Users and third-party libraries may implement their own execution contexts, for example to provide different scheduling guarantees, or to natively support other types of I/O objects, such as files. This proposal would allow users to create the standard I/O objects, such as sockets, to be associated with these custom execution contexts. However, although constructing the socket with a custom execution context will provide the correct, expected behaviour, there is no guarantee that it will operate as efficiently as a socket associated with a native execution context.
Some operating systems have multiple potential implementation strategies for the I/O objects, like sockets. For example, on Windows we can choose to have asynchronous notifications delivered via a completion port to a user-created thread, or we can have them delivered directly to the system thread pool. An implementer may want to make both of these strategies available, via the io_context
and system_context
execution contexts, respectively. For example:
io_context my_context; tcp::socket my_socket_1(my_context); // Implementation uses a completion port. // ... tcp::socket my_socket_2(system_executor{}); // Implementation uses the system thread pool.
Libraries can also provide additional native execution contexts as implementation-specific extensions.
In some use cases, all asynchronous operations on a given I/O object are performed using the same executor. One common example of this is when using a strand. This proposal allows this executor to be specified once, when the I/O object is constructed. For example:
io_context my_context; // ... strand<io_context::executor_type> my_strand(my_context.get_executor()); tcp::socket my_socket(my_strand); // ... my_socket.async_receive(my_buffer, my_handler); // my_handler is invoked in the strand
The Executor
template parameter also offers an opportunity to specialize the I/O object types for specific executor types. For example, some platforms support vendor-specific kernel bypass APIs for I/O such as sockets, enabling significantly lower latency and higher throughput when compared to system calls. Furthermore, these APIs are often limited to supporting only TCP and UDP. A library or user may therefore choose to define an executor type that represents this API as a venue for I/O execution:
class vendor_xyz_kernel_bypass_executor { // ... };
and specialize the socket templates for this executor type:
template<> class basic_socket<ip::tcp, vendor_xyz_kernel_bypass_executor> { // ... }; template<> class basic_datagram_socket<ip::tcp, vendor_xyz_kernel_bypass_executor> : public basic_socket<ip::tcp, vendor_xyz_kernel_bypass_executor> { // ... template<class ConstBufferSequence> void send(const ConstBufferSequence& buffers) { // Implemented using vendor_xyz_send(). // ... } // ... };
A new Executor
template parameter is added to the following templates:
basic_waitable_timer
basic_socket
basic_datagram_socket
basic_stream_socket
basic_socket_acceptor
basic_socket_streambuf
basic_socket_iostream
ip::basic_resolver
To enable convenient interoperability with arbitrary executors and execution contexts, the template parameter is defaulted to the executor
polymorphic wrapper. For example:
template <class Protocol, class Executor = executor> class basic_socket;
For each of:
basic_waitable_timer
basic_socket
basic_socket_acceptor
ip::basic_resolver
the existing nested type executor_type
is altered to refer to the Executor
type:
template <class Protocol, class Executor> class basic_socket : public socket_base { public: using executor_type =io_context::executor_typeExecutor; // ... };
For the remaining four templates:
basic_datagram_socket
basic_stream_socket
basic_socket_streambuf
basic_socket_iostream
the nested type executor_type
is added:
template <class Protocol, class Executor> class basic_stream_socket : public basic_socket<Protocol, Executor> { public: using executor_type = Executor; // ... };
For each of the following templates:
basic_waitable_timer
basic_socket
basic_datagram_socket
basic_stream_socket
basic_socket_acceptor
ip::basic_resolver
a nested template type alias rebind_executor
is added:
template<class Protocol, class Executor> class basic_socket : public socket_base { // ... /// The socket type when rebound to the specified executor. template<class OtherExecutor> using rebind_executor = basic_socket<Protocol, OtherExecutor>; // ... };
This enables computation of the appropriate class type for different I/O executor types.
For each of the following templates:
basic_waitable_timer
basic_socket
basic_datagram_socket
basic_stream_socket
basic_socket_acceptor
ip::basic_resolver
every constructor with an io_context&
parameter is replaced with two constructors:
template<class Protocol, class Executor> class basic_socket : public socket_base { // ...basic_socket(io_context& ctx, const protocol_type& protocol);basic_socket(const executor_type& ex, const protocol_type& protocol); template<class ExecutionContext> basic_socket(ExecutionContext& ctx, const protocol_type& protocol); // ... };
The second of these constructors shall not participate in overload resolution unless is_convertible<ExecutionContext&, execution_context&>::value
is true
, and is_constructible<executor_type, typename ExecutionContext::executor_type>::value
is true
.
An OtherExecutor
template parameter is added as required to the converting move constructors of the following classes:
basic_socket
basic_datagram_socket
basic_stream_socket
basic_socket_acceptor
For example:
template<class Protocol, class Executor> class basic_socket : public socket_base { // ... template<class OtherProtocol, class OtherExecutor> basic_socket(basic_socket<OtherProtocol, OtherExecutor>&& rhs); // ... };
This constructor shall not participate in overload resolution unless OtherProtocol
is implicitly convertible to Protocol
, and OtherExecutor
is implicitly convertible to Executor
.
An OtherExecutor
template parameter is added as required to the converting move assignment operators of the following classes:
basic_socket
basic_datagram_socket
basic_stream_socket
basic_socket_acceptor
For example:
template<class Protocol, class Executor> class basic_socket : public socket_base { // ... template<class OtherProtocol, class OtherExecutor> basic_socket& operator=(basic_socket<OtherProtocol, OtherExecutor>&& rhs); // ... };
This assignment operator shall not participate in overload resolution unless OtherProtocol
is implicitly convertible to Protocol
, and OtherExecutor
is implicitly convertible to Executor
.
The AcceptableProtocol
type requirements, ip::tcp
class, and ip::udp
class each acquire a new nested template type alias socket_for
. It can be used to create a protocol socket with a specific executor type:
net::ip::tcp::socket_for<my_executor> sock = /* ... */;
The template basic_socket_acceptor
gains a new nested template type alias socket_type_for
:
template<class AcceptableProtocol, class Executor> class basic_socket_acceptor : public socket_base { // ... template<class OtherExecutor> using socket_type_for = typename AcceptableProtocol::socket_for<OtherExecutor>; // ... };
This enables computation of the appropriate socket type produced when accepting on different I/O executor types.
Every overload of member functions accept
and async_accept
with an io_context&
parameter is replaced with two overloads:
template<class AcceptableProtocol, class Executor> class basic_socket_acceptor : public socket_base { // ...socket_type accept(io_context& ctx);template<class OtherExecutor> socket_type_for<OtherExecutor> accept(const OtherExecutor& ex); template<class ExecutionContext> socket_type_for<typename ExecutionContext::executor_type> accept(ExecutionContext& ctx); // ... };
The second of these overloads shall not participate in overload resolution unless is_convertible<ExecutionContext&, execution_context&>::value
is true
, and is_constructible<executor_type, typename ExecutionContext::executor_type>::value
is true
.
An Executor
template parameter is added as required to the connect
and async_connect
functions. For example:
template<class Protocol, class Executor, class EndpointSequence> typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s, const EndpointSequence& endpoints);
The basic_socket_streambuf
default constructor is modified so that it shall not participate in overload resolution unless is_constructible<executor_type, io_context::executor_type>::value
is true
.
The basic_socket_iostream
specification is modified so that the executor_type
type is passed to all uses of basic_socket_streambuf
, as required. The basic_socket_iostream
default constructor is modified so that it shall not participate in overload resolution unless is_default_constructible<basic_socket_streambuf<protocol_type, clock_type, wait_traits_type, executor_type>>::value
is true
.
This section is intended to provide implementation suggestions only, and is not intended to be prescriptive or exhaustive.
Implementations may use an execution context service as a container for the backend implementation.
class __socket_backend : public execution_context::service { // ... };
This service would be "used" by an I/O object implementation, and is automatically created on first use:
template <class Protocol, class Executor> class basic_socket : public socket_base { public: using executor_type = Executor; // ... explicit basic_socket(const executor_type& ex) { auto& backend = use_service<__socket_backend>(ex.context()); // ... } // ... };
This allows the I/O object to be used with arbitrary execution contexts.
A native execution context preemptively performs service creation by calling make_service
. It can use this opportunity to pass additional constructor arguments that initialise the backend in the native mode, rather than the default:
class io_context : public execution_context { public: // ... io_context() { make_service<__socket_backend>(*this, __io_context_backend_tag{}); } // ... };
Alternatively, implementations may use a class hierarchy of services, and virtual functions, to select the desired behaviour:
class __socket_backend : public execution_context::service { public: using key_type = __socket_backend; // ... virtual void backend_function(/* ... */); // ... }; class __io_socket_backend : public __socket_backend { public: // ... void backend_function(/* ... */) override; // ... }; class io_context : public execution_context { public: // ... io_context() { make_service<__io_socket_backend>(*this); } // ... };
In both approaches, the existing service, with its native backend, is obtained by the I/O object constructor.
Specification of the polymorphic executor as the default I/O executor, while improving usability, has a non-zero impact on performance. This impact can be mitigated by having an I/O object's constructor detect its own well-known native executor types (e.g. by using executor::target_type
). With this information, the overhead can then be limited to simple branching, rather than the memory allocations and virtual functions that would likely be required for the type-erased function objects.
Users can avoid this overhead completely by explicitly specifying the I/O executor type:
io_context my_context; basic_stream_socket<tcp, io_context::executor_type> my_socket(my_context);
This design change was implemented in Asio 1.13.0, and was delivered in the Boost 1.70 release as the stable Asio version 1.14.0.
These changes are relative to N4771.
Update the <experimental/netfwd>
synopsis [fwd.decl.synop] as follows:
namespace std { namespace experimental { namespace net { inline namespace v1 { class execution_context; template<class T, class Executor> class executor_binder; template<class Executor> class executor_work_guard; class system_executor; class executor; template<class Executor> class strand; class io_context; template<class Clock> struct wait_traits; template<class Clock, class WaitTraits = wait_traits<Clock>, class Executor = executor> class basic_waitable_timer; using system_timer = basic_waitable_timer<chrono::system_clock>; using steady_timer = basic_waitable_timer<chrono::steady_clock>; using high_resolution_timer = basic_waitable_timer<chrono::high_resolution_clock>; template<class Protocol, class Executor = executor> class basic_socket; template<class Protocol, class Executor = executor> class basic_datagram_socket; template<class Protocol, class Executor = executor> class basic_stream_socket; template<class Protocol, class Executor = executor> class basic_socket_acceptor; template<class Protocol, class Clock = chrono::steady_clock, class WaitTraits = wait_traits<Clock>, class Executor = executor> class basic_socket_streambuf; template<class Protocol, class Clock = chrono::steady_clock, class WaitTraits = wait_traits<Clock>, class Executor = executor> class basic_socket_iostream; namespace ip { class address; class address_v4; class address_v6; template<class Address> class basic_address_iterator; using address_v4_iterator = basic_address_iterator<address_v4>; using address_v6_iterator = basic_address_iterator<address_v6>; template<class Address> class basic_address_range; using address_v4_range = basic_address_range<address_v4>; using address_v6_range = basic_address_range<address_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 Executor = executor> class basic_resolver; class tcp; class udp; } // namespace ip } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
Update the <experimental/timer>
synopsis [timer.synop] as follows:
[...]
template<class Clock, class WaitTraits = wait_traits<Clock>, class Executor = executor> class basic_waitable_timer;
Update the class template basic_waitable_timer
[timer.waitable] as follows:
namespace std { namespace experimental { namespace net { inline namespace v1 { template<class Clock, class WaitTraits = wait_traits<Clock>, class Executor = executor> class basic_waitable_timer { public: // types: using executor_type =io_context::executor_typeExecutor; using clock_type = Clock; using duration = typename clock_type::duration; using time_point = typename clock_type::time_point; using traits_type = WaitTraits; template<class OtherExecutor> using rebind_executor = basic_waitable_timer<Clock, WaitTraits, OtherExecutor>; // [timer.waitable.cons], construct / copy / destroy: explicit basic_waitable_timer(io_context& ctxconst executor_type& ex); template<class ExecutionContext> explicit basic_waitable_timer(ExecutionContext& ctx); basic_waitable_timer(io_context& ctxconst executor_type& ex, const time_point& t); template<class ExecutionContext> basic_waitable_timer(ExecutionContext& ctx, const time_point& t); basic_waitable_timer(io_context& ctxconst executor_type& ex, const duration& d); template<class ExecutionContext> basic_waitable_timer(ExecutionContext& ctx, const duration& d); basic_waitable_timer(const basic_waitable_timer&) = delete; basic_waitable_timer(basic_waitable_timer&& rhs);
Update the basic_waitable_timer
constructors [timer.waitable.cons] as follows:
explicit basic_waitable_timer(io_context& ctxconst executor_type& ex);
template<class ExecutionContext> explicit basic_waitable_timer(ExecutionContext& ctx);
basic_waitable_timer(io_context& ctxconst executor_type& ex, const time_point& t);
template<class ExecutionContext> basic_waitable_timer(ExecutionContext& ctx, const time_point& t);
basic_waitable_timer(io_context& ctxconst executor_type& ex, const duration& d);
template<class ExecutionContext> basic_waitable_timer(ExecutionContext& ctx, const duration& d);
Update the <experimental/socket>
synopsis [socket.synop] as follows:
namespace std { namespace experimental { namespace net { inline namespace v1 { enum class socket_errc { already_open = an implementation-defined non-zero value, not_found = an implementation-defined non-zero value }; const error_category& socket_category() noexcept; error_code make_error_code(socket_errc e) noexcept; error_condition make_error_condition(socket_errc e) noexcept; // Sockets: class socket_base; template<class Protocol, class Executor = executor> class basic_socket; template<class Protocol, class Executor = executor> class basic_datagram_socket; template<class Protocol, class Executor = executor> class basic_stream_socket; template<class Protocol, class Executor = executor> class basic_socket_acceptor; // [socket.iostreams], Socket streams: template<class Protocol, class Clock = chrono::steady_clock, class WaitTraits = wait_traits<Clock>, class Executor = executor> class basic_socket_streambuf; template<class Protocol, class Clock = chrono::steady_clock, class WaitTraits = wait_traits<Clock>, class Executor = executor> class basic_socket_iostream; // [socket.algo.connect], synchronous connect operations: template<class Protocol, class Executor, class EndpointSequence> typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s, const EndpointSequence& endpoints); template<class Protocol, class Executor, class EndpointSequence> typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s, const EndpointSequence& endpoints, error_code& ec); template<class Protocol, class Executor, class EndpointSequence, class ConnectCondition> typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s, const EndpointSequence& endpoints, ConnectCondition c); template<class Protocol, class Executor, class EndpointSequence, class ConnectCondition> typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s, const EndpointSequence& endpoints, ConnectCondition c, error_code& ec); template<class Protocol, class Executor, class InputIterator> InputIterator connect(basic_socket<Protocol, Executor>& s, InputIterator first, InputIterator last); template<class Protocol, class Executor, class InputIterator> InputIterator connect(basic_socket<Protocol, Executor>& s, InputIterator first, InputIterator last, error_code& ec); template<class Protocol, class Executor, class InputIterator, class ConnectCondition> InputIterator connect(basic_socket<Protocol, Executor>& s, InputIterator first, InputIterator last, ConnectCondition c); template<class Protocol, class Executor, class InputIterator, class ConnectCondition> InputIterator connect(basic_socket<Protocol, Executor>& s, InputIterator first, InputIterator last, ConnectCondition c, error_code& ec); // [socket.algo.async.connect], asynchronous connect operations: template<class Protocol, class Executor, class EndpointSequence, class CompletionToken> DEDUCED async_connect(basic_socket<Protocol, Executor>& s, const EndpointSequence& endpoints, CompletionToken&& token); template<class Protocol, class Executor, class EndpointSequence, class ConnectCondition, class CompletionToken> DEDUCED async_connect(basic_socket<Protocol, Executor>& s, const EndpointSequence& endpoints, ConnectCondition c, CompletionToken&& token); template<class Protocol, class Executor, class InputIterator, class CompletionToken> DEDUCED async_connect(basic_socket<Protocol, Executor>& s, InputIterator first, InputIterator last, CompletionToken&& token); template<class Protocol, class Executor, class InputIterator, class ConnectCondition, class CompletionToken> DEDUCED async_connect(basic_socket<Protocol, Executor>& s, InputIterator first, InputIterator last, ConnectCondition c, CompletionToken&& token); } // inline namespace v1 } // namespace net } // namespace experimental template<> struct is_error_code_enum<experimental::net::v1::socket_errc> : public true_type {}; } // namespace std
Update the AcceptableProtocol
requirements [socket.reqmts.acceptableprotocol] as follows:
expression | return type | assertion/note pre/post-conditions |
X::socket | A type that satisfies the requirements of Destructible (C++2014[destructible]) and MoveConstructible (C++2014[moveconstructible]), and that is publicly and unambiguously derived from basic_socket<X>. | |
X::socket_for<E> | A type that satisfies the requirements of Destructible (C++2014[destructible]) and MoveConstructible (C++2014[moveconstructible]), and that is publicly and unambiguously derived from basic_socket<X, E>. |
Update the class socket_base
[socket.base] as follows:
namespace std { namespace experimental { namespace net { inline namespace v1 { class socket_base { public: class broadcast; class debug; class do_not_route; class keep_alive; class linger; class out_of_band_inline; class receive_buffer_size; class receive_low_watermark; class reuse_address; class send_buffer_size; class send_low_watermark; using shutdown_type = T1; static constexpr shutdown_type shutdown_receive; static constexpr shutdown_type shutdown_send; static constexpr shutdown_type shutdown_both; using wait_type = T2; static constexpr wait_type wait_read; static constexpr wait_type wait_write; static constexpr wait_type wait_error; using message_flags = T3; static constexpr message_flags message_peek; static constexpr message_flags message_out_of_band; static constexpr message_flags message_do_not_route; static const int max_listen_connections; protected: socket_base(); ~socket_base(); }; } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
Update the class template basic_socket
[socket.basic] as follows:
namespace std { namespace experimental { namespace net { inline namespace v1 { template<class Protocol, class Executor> class basic_socket : public socket_base { public: // types: using executor_type =io_context::executor_typeExecutor; using native_handle_type = implementation-defined; // see [socket.reqmts.native] using protocol_type = Protocol; using endpoint_type = typename protocol_type::endpoint; template<class OtherExecutor> using rebind_executor = basic_socket<Protocol, OtherExecutor>; // [socket.basic.ops], basic_socket operations: executor_type get_executor() noexcept; native_handle_type native_handle(); // see [socket.reqmts.native] void open(const protocol_type& protocol = protocol_type()); void open(const protocol_type& protocol, error_code& ec); void assign(const protocol_type& protocol, const native_handle_type& native_socket); // see [socket.reqmts.native] void assign(const protocol_type& protocol, const native_handle_type& native_socket, error_code& ec); // see [socket.reqmts.native] native_handle_type release(); // see [socket.reqmts.native] native_handle_type release(error_code& ec); // see [socket.reqmts.native] bool is_open() const noexcept; void close(); void close(error_code& ec); void cancel(); void cancel(error_code& ec); template<class SettableSocketOption> void set_option(const SettableSocketOption& option); template<class SettableSocketOption> void set_option(const SettableSocketOption& option, error_code& ec); template<class GettableSocketOption> void get_option(GettableSocketOption& option) const; template<class GettableSocketOption> void get_option(GettableSocketOption& option, error_code& ec) const; template<class IoControlCommand> void io_control(IoControlCommand& command); template<class IoControlCommand> void io_control(IoControlCommand& command, error_code& ec); void non_blocking(bool mode); void non_blocking(bool mode, error_code& ec); bool non_blocking() const; void native_non_blocking(bool mode); void native_non_blocking(bool mode, error_code& ec); bool native_non_blocking() const; bool at_mark() const; bool at_mark(error_code& ec) const; size_t available() const; size_t available(error_code& ec) const; void bind(const endpoint_type& endpoint); void bind(const endpoint_type& endpoint, error_code& ec); void shutdown(shutdown_type what); void shutdown(shutdown_type what, error_code& ec); endpoint_type local_endpoint() const; endpoint_type local_endpoint(error_code& ec) const; endpoint_type remote_endpoint() const; endpoint_type remote_endpoint(error_code& ec) const; void connect(const endpoint_type& endpoint); void connect(const endpoint_type& endpoint, error_code& ec); template<class CompletionToken> DEDUCED async_connect(const endpoint_type& endpoint, CompletionToken&& token); void wait(wait_type w); void wait(wait_type w, error_code& ec); template<class CompletionToken> DEDUCED async_wait(wait_type w, CompletionToken&& token); protected: // [socket.basic.cons], construct / copy / destroy: explicit basic_socket(io_context& ctxconst executor_type& ex); template<class ExecutionContext> explicit basic_socket(ExecutionContext& ctx); basic_socket(io_context& ctxconst executor_type& ex, const protocol_type& protocol); template<class ExecutionContext> basic_socket(ExecutionContext& ctx, const protocol_type& protocol); basic_socket(io_context& ctxconst executor_type& ex, const endpoint_type& endpoint); template<class ExecutionContext> basic_socket(ExecutionContext& ctx, const endpoint_type& endpoint); basic_socket(io_context& ctxconst executor_type& ex, const protocol_type& protocol, const native_handle_type& native_socket); // see [socket.reqmts.native] template<class ExecutionContext> basic_socket(ExecutionContext& ctx, const protocol_type& protocol, const native_handle_type& native_socket); // see [socket.reqmts.native] basic_socket(const basic_socket&) = delete; basic_socket(basic_socket&& rhs); template<class OtherProtocol, class OtherExecutor> basic_socket(basic_socket<OtherProtocol, OtherExecutor>&& rhs); ~basic_socket(); basic_socket& operator=(const basic_socket&) = delete; basic_socket& operator=(basic_socket&& rhs); template<class OtherProtocol, class OtherExecutor> basic_socket& operator=(basic_socket<OtherProtocol, OtherExecutor>&& rhs); private: protocol_type protocol_; // exposition only }; } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
Update the basic_socket
constructors [socket.basic.cons] as follows:
explicit basic_socket(io_context& ctxconst executor_type& ex);
template<class ExecutionContext> explicit basic_socket(ExecutionContext& ctx);
basic_socket(io_context& ctxconst executor_type& ex, const protocol_type& protocol);
template<class ExecutionContext> explicit basic_socket(ExecutionContext& ctx, const protocol_type& protocol);
basic_socket(io_context& ctxconst executor_type& ex, const endpoint_type& endpoint);
template<class ExecutionContext> explicit basic_socket(ExecutionContext& ctx, const endpoint_type& endpoint);
basic_socket(io_context& ctxconst executor_type& ex, const protocol_type& protocol, const native_handle_type& native_socket);
template<class ExecutionContext> basic_socket(ExecutionContext& ctx, const protocol_type& protocol, const native_handle_type& native_socket);
basic_socket(basic_socket&& rhs);
template<class OtherProtocol, class OtherExecutor> basic_socket(basic_socket<OtherProtocol, OtherExecutor>&& rhs);
Update the basic_socket
assignment operators [socket.basic.assign] as follows:
basic_socket& operator=(basic_socket&& rhs);
template<class OtherProtocol, class OtherExecutor> basic_socket& operator=(basic_socket<OtherProtocol, OtherExecutor>&& rhs);
Update the class template basic_datagram_socket
[socket.basic] as follows:
namespace std { namespace experimental { namespace net { inline namespace v1 { template<class Protocol, class Executor> class basic_datagram_socket : public basic_socket<Protocol, Executor> { public: // types: using executor_type = Executor; using native_handle_type = implementation-defined; // see [socket.reqmts.native] using protocol_type = Protocol; using endpoint_type = typename protocol_type::endpoint; template<class OtherExecutor> using rebind_executor = basic_datagram_socket<Protocol, OtherExecutor>; // [socket.dgram.cons], construct / copy / destroy: explicit basic_datagram_socket(io_context& ctxconst executor_type& ex); template<class ExecutionContext> explicit basic_datagram_socket(ExecutionContext& ctx); basic_datagram_socket(io_context& ctxconst executor_type& ex, const protocol_type& protocol); template<class ExecutionContext> basic_datagram_socket(ExecutionContext& ctx, const protocol_type& protocol); basic_datagram_socket(io_context& ctxconst executor_type& ex, const endpoint_type& endpoint); template<class ExecutionContext> basic_datagram_socket(ExecutionContext& ctx, const endpoint_type& endpoint); basic_datagram_socket(io_context& ctxconst executor_type& ex, const protocol_type& protocol, const native_handle_type& native_socket); template<class ExecutionContext> basic_datagram_socket(ExecutionContext& ctx, const protocol_type& protocol, const native_handle_type& native_socket); basic_datagram_socket(const basic_datagram_socket&) = delete; basic_datagram_socket(basic_datagram_socket&& rhs); template<class OtherProtocol, class OtherExecutor> basic_datagram_socket(basic_datagram_socket<OtherProtocol, OtherExecutor>&& rhs); ~basic_datagram_socket(); basic_datagram_socket& operator=(const basic_datagram_socket&) = delete; basic_datagram_socket& operator=(basic_datagram_socket&& rhs); template<class OtherProtocol, class OtherExecutor> basic_datagram_socket& operator=(basic_datagram_socket<OtherProtocol, OtherExecutor>&& rhs);
[...]
Update the basic_datagram_socket
constructors [socket.dgram.cons] as follows:
explicit basic_datagram_socket(io_context& ctxconst executor_type& ex);
template<class ExecutionContext> explicit basic_datagram_socket(ExecutionContext& ctx);
basic_datagram_socket(io_context& ctxconst executor_type& ex, const protocol_type& protocol);
template<class ExecutionContext> explicit basic_datagram_socket(ExecutionContext& ctx, const protocol_type& protocol);
basic_datagram_socket(io_context& ctxconst executor_type& ex, const endpoint_type& endpoint);
template<class ExecutionContext> explicit basic_datagram_socket(ExecutionContext& ctx, const endpoint_type& endpoint);
basic_datagram_socket(io_context& ctx, const protocol_type& protocol, const native_handle_type& native_socket);
template<class ExecutionContext> basic_datagram_socket(ExecutionContext& ctx, const protocol_type& protocol, const native_handle_type& native_socket);
basic_datagram_socket(basic_datagram_socket&& rhs);
template<class OtherProtocol, class OtherExecutor> basic_datagram_socket(basic_datagram_socket<OtherProtocol, OtherExecutor>&& rhs);
Update the basic_datagram_socket
assignment operators [socket.dgram.assign] as follows:
basic_datagram_socket& operator=(basic_datagram_socket&& rhs);
template<class OtherProtocol, class OtherExecutor> basic_datagram_socket& operator=(basic_datagram_socket<OtherProtocol, OtherExecutor>&& rhs);
Update the class template basic_stream_socket
[socket.stream] as follows:
namespace std { namespace experimental { namespace net { inline namespace v1 { template<class Protocol, class Executor> class basic_stream_socket : public basic_socket<Protocol, Executor> { public: // types: using executor_type = Executor; using native_handle_type = implementation-defined; // see [socket.reqmts.native] using protocol_type = Protocol; using endpoint_type = typename protocol_type::endpoint; template<class OtherExecutor> using rebind_executor = basic_stream_socket<Protocol, OtherExecutor>; // [socket.stream.cons], construct / copy / destroy: explicit basic_stream_socket(io_context& ctxconst executor_type& ex); template<class ExecutionContext> explicit basic_stream_socket(ExecutionContext& ctx); basic_stream_socket(io_context& ctxconst executor_type& ex, const protocol_type& protocol); template<class ExecutionContext> basic_stream_socket(ExecutionContext& ctx, const protocol_type& protocol); basic_stream_socket(io_context& ctxconst executor_type& ex, const endpoint_type& endpoint); template<class ExecutionContext> basic_stream_socket(ExecutionContext& ctx, const endpoint_type& endpoint); basic_stream_socket(io_context& ctxconst executor_type& ex, const protocol_type& protocol, const native_handle_type& native_socket); template<class ExecutionContext> basic_stream_socket(ExecutionContext& ctx, const protocol_type& protocol, const native_handle_type& native_socket); basic_stream_socket(const basic_stream_socket&) = delete; basic_stream_socket(basic_stream_socket&& rhs); template<class OtherProtocol, class OtherExecutor> basic_stream_socket(basic_stream_socket<OtherProtocol, OtherExecutor>&& rhs); ~basic_stream_socket(); basic_stream_socket& operator=(const basic_stream_socket&) = delete; basic_stream_socket& operator=(basic_stream_socket&& rhs); template<class OtherProtocol, class OtherExecutor> basic_stream_socket& operator=(basic_stream_socket<OtherProtocol, OtherExecutor>&& rhs);
[...]
Update the basic_stream_socket
constructors [socket.stream.cons] as follows:
explicit basic_stream_socket(io_context& ctxconst executor_type& ex);
template<class ExecutionContext> explicit basic_stream_socket(ExecutionContext& ctx);
basic_stream_socket(io_context& ctxconst executor_type& ex, const protocol_type& protocol);
template<class ExecutionContext> explicit basic_stream_socket(ExecutionContext& ctx, const protocol_type& protocol);
basic_stream_socket(io_context& ctxconst executor_type& ex, const endpoint_type& endpoint);
template<class ExecutionContext> explicit basic_stream_socket(ExecutionContext& ctx, const endpoint_type& endpoint);
basic_stream_socket(io_context& ctxconst executor_type& ex, const protocol_type& protocol, const native_handle_type& native_socket);
template<class ExecutionContext> basic_stream_socket(ExecutionContext& ctx, const protocol_type& protocol, const native_handle_type& native_socket);
basic_stream_socket(basic_stream_socket&& rhs);
template<class OtherProtocol, class OtherExecutor> basic_stream_socket(basic_stream_socket<OtherProtocol, OtherExecutor>&& rhs);
Update the basic_stream_socket
assignment operators [socket.stream.assign] as follows:
basic_stream_socket& operator=(basic_stream_socket&& rhs);
template<class OtherProtocol, class OtherExecutor> basic_stream_socket& operator=(basic_stream_socket<OtherProtocol, OtherExecutor>&& rhs);
Update the class template basic_socket_acceptor
[socket.acceptor] as follows:
namespace std { namespace experimental { namespace net { inline namespace v1 { template<class AcceptableProtocol, class Executor> class basic_socket_acceptor : public socket_base { public: // types: using executor_type =io_context::executor_typeExecutor; using native_handle_type = implementation-defined; // see [socket.reqmts.native] using protocol_type = AcceptableProtocol; using endpoint_type = typename protocol_type::endpoint; using socket_type = typename protocol_type::socket;template<class OtherExecutor> using socket_type_for = typename protocol_type::socket_for<OtherExecutor>;template<class OtherExecutor> using rebind_executor = basic_socket_acceptor<AcceptableProtocol, OtherExecutor>; // [socket.acceptor.cons], construct / copy / destroy: explicit basic_socket_acceptor(io_context& ctxconst executor_type& ex); template<class ExecutionContext> explicit basic_socket_acceptor(ExecutionContext& ctx); basic_socket_acceptor(io_context& ctxconst executor_type& ex, const protocol_type& protocol); template<class ExecutionContext> basic_socket_acceptor(ExecutionContext& ctx, const protocol_type& protocol); basic_socket_acceptor(io_context& ctxconst executor_type& ex, const endpoint_type& endpoint, bool reuse_addr = true); template<class ExecutionContext> basic_socket_acceptor(ExecutionContext& ctx, const endpoint_type& endpoint, bool reuse_addr = true); basic_socket_acceptor(io_context& ctxconst executor_type& ex, const protocol_type& protocol, const native_handle_type& native_socket); template<class ExecutionContext> basic_socket_acceptor(ExecutionContext& ctx, const protocol_type& protocol, const native_handle_type& native_socket); basic_socket_acceptor(const basic_socket_acceptor&) = delete; basic_socket_acceptor(basic_socket_acceptor&& rhs); template<class OtherProtocol, class OtherExecutor> basic_socket_acceptor(basic_socket_acceptor<OtherProtocol, OtherExecutor>&& rhs); ~basic_socket_acceptor(); basic_socket_acceptor& operator=(const basic_socket_acceptor&) = delete; basic_socket_acceptor& operator=(basic_socket_acceptor&& rhs); template<class OtherProtocol, class OtherExecutor> basic_socket_acceptor& operator=(basic_socket_acceptor<OtherProtocol, OtherExecutor>&& rhs); // [socket.acceptor.ops], basic_socket_acceptor operations: executor_type get_executor() noexcept; native_handle_type native_handle(); // see [socket.reqmts.native] void open(const protocol_type& protocol = protocol_type()); void open(const protocol_type& protocol, error_code& ec); void assign(const protocol_type& protocol, const native_handle_type& native_acceptor); // see [socket.reqmts.native] void assign(const protocol_type& protocol, const native_handle_type& native_acceptor, error_code& ec); // see [socket.reqmts.native] native_handle_type release(); // see [socket.reqmts.native] native_handle_type release(error_code& ec); // see [socket.reqmts.native] bool is_open() const noexcept; void close(); void close(error_code& ec); void cancel(); void cancel(error_code& ec); template<class SettableSocketOption> void set_option(const SettableSocketOption& option); template<class SettableSocketOption> void set_option(const SettableSocketOption& option, error_code& ec); template<class GettableSocketOption> void get_option(GettableSocketOption& option) const; template<class GettableSocketOption> void get_option(GettableSocketOption& option, error_code& ec) const; template<class IoControlCommand> void io_control(IoControlCommand& command); template<class IoControlCommand> void io_control(IoControlCommand& command, error_code& ec); void non_blocking(bool mode); void non_blocking(bool mode, error_code& ec); bool non_blocking() const; void native_non_blocking(bool mode); void native_non_blocking(bool mode, error_code& ec); bool native_non_blocking() const; void bind(const endpoint_type& endpoint); void bind(const endpoint_type& endpoint, error_code& ec); void listen(int backlog = max_listen_connections); void listen(int backlog, error_code& ec); endpoint_type local_endpoint() const; endpoint_type local_endpoint(error_code& ec) const; void enable_connection_aborted(bool mode); bool enable_connection_aborted() const; socket_type accept(); socket_type accept(error_code& ec);socket_type accept(io_context& ctx);socket_type accept(io_context& ctx, error_code& ec);template<class OtherExecutor> socket_type_for<OtherExecutor> accept(const OtherExecutor& ex); template<class OtherExecutor> socket_type_for<OtherExecutor> accept(const OtherExecutor& ex, error_code& ec); template<class ExecutionContext> socket_type_for<typename ExecutionContext::executor_type> accept(ExecutionContext& ctx); template<class ExecutionContext> socket_type_for<typename ExecutionContext::executor_type> accept(ExecutionContext& ctx, error_code& ec);template<class CompletionToken> DEDUCED async_accept(CompletionToken&& token);template<class CompletionToken>DEDUCED async_accept(io_context& ctx, CompletionToken&& token);template<class OtherExecutor, class CompletionToken> DEDUCED async_accept(const OtherExecutor& ex, CompletionToken&& token); template<class ExecutionContext, class CompletionToken> DEDUCED async_accept(ExecutionContext& ctx, CompletionToken&& token); socket_type accept(endpoint_type& endpoint); socket_type accept(endpoint_type& endpoint, error_code& ec);socket_type accept(io_context& ctx, endpoint_type& endpoint);socket_type accept(io_context& ctx, endpoint_type& endpoint,error_code& ec);template<class OtherExecutor> socket_type_for<OtherExecutor> accept(const OtherExecutor& ex, endpoint_type& endpoint); template<class OtherExecutor> socket_type_for<OtherExecutor> accept(const OtherExecutor& ex, endpoint_type& endpoint, error_code& ec); template<class ExecutionContext> socket_type_for<typename ExecutionContext::executor_type> accept(ExecutionContext& ctx, endpoint_type& endpoint); template<class ExecutionContext> socket_type_for<typename ExecutionContext::executor_type> accept(ExecutionContext& ctx, endpoint_type& endpoint, error_code& ec);template<class CompletionToken> DEDUCED async_accept(endpoint_type& endpoint, CompletionToken&& token);template<class CompletionToken>DEDUCED async_accept(io_context& ctx, endpoint_type& endpoint,CompletionToken&& token);template<class OtherExecutor, class CompletionToken> DEDUCED async_accept(const OtherExecutor& ex, endpoint_type& endpoint, CompletionToken&& token); template<class ExecutionContext, class CompletionToken> DEDUCED async_accept(ExecutionContext& ctx, endpoint_type& endpoint, CompletionToken&& token);
Update the basic_socket_acceptor
constructors [socket.stream.cons] as follows:
explicit basic_socket_acceptor(io_context& ctxconst executor_type& ex);
template<class ExecutionContext> explicit basic_socket_acceptor(ExecutionContext& ctx);
basic_socket_acceptor(io_context& ctxconst executor_type& ex, const protocol_type& protocol);
template<class ExecutionContext> explicit basic_socket_acceptor(ExecutionContext& ctx, const protocol_type& protocol);
basic_socket_acceptor(io_context& ctxconst executor_type& ex, const endpoint_type& endpoint, bool reuse_addr = true);
open(endpoint.protocol()); if (reuse_addr) set_option(reuse_address(true)); bind(endpoint); listen();
template<class ExecutionContext> explicit basic_socket_acceptor(ExecutionContext& ctx, const endpoint_type& endpoint, bool reuse_addr = true);
basic_socket_acceptor(io_context& ctxconst executor_type& ex, const protocol_type& protocol, const native_handle_type& native_acceptor);
template<class ExecutionContext> basic_socket_acceptor(ExecutionContext& ctx, const protocol_type& protocol, const native_handle_type& native_socket);
basic_socket_acceptor(basic_socket_acceptor&& rhs);
template<class OtherProtocol, class OtherExecutor> basic_socket_acceptor(basic_socket_acceptor<OtherProtocol, OtherExecutor>&& rhs);
Update the basic_socket_acceptor
assignment operators [socket.stream.assign] as follows:
basic_socket_acceptor& operator=(basic_socket_acceptor&& rhs);
template<class OtherProtocol, class OtherExecutor> basic_socket_acceptor& operator=(basic_socket_acceptor<OtherProtocol, OtherExecutor>&& rhs);
Update the basic_socket_acceptor
operations [socket.stream.ops] as follows:
[...]
socket_type accept(); socket_type accept(error_code& ec);
socket_type accept(io_context& ctx);socket_type accept(io_context& ctx, error_code& ec);template<class OtherExecutor> socket_type_for<OtherExecutor> accept(const OtherExecutor& ex); template<class OtherExecutor> socket_type_for<OtherExecutor> accept(const OtherExecutor& ex, error_code& ec);
native_handle_type h = accept(native_handle(), nullptr, 0);
template<class ExecutionContext> socket_type_for<typename ExecutionContext::executor_type> accept(ExecutionContext& ctx); template<class ExecutionContext> socket_type_for<typename ExecutionContext::executor_type> accept(ExecutionContext& ctx, error_code& ec);
template<class CompletionToken> DEDUCED async_accept(CompletionToken&& token);
template<class CompletionToken>DEDUCED async_accept(io_context& ctx, CompletionToken&& token);template<class OtherExecutor, class CompletionToken> DEDUCED async_accept(const OtherExecutor& ex, CompletionToken&& token);
native_handle_type h = accept(native_handle(), nullptr, 0);
template<class ExecutionContext, class CompletionToken> DEDUCED async_accept(ExecutionContext& ctx, CompletionToken&& token);
async_accept(ctx.get_executor(), forward<CompletionToken>(token))
socket_type accept(endpoint_type& endpoint); socket_type accept(endpoint_type& endpoint, error_code& ec);
socket_type accept(io_context& ctx, endpoint_type& endpoint);socket_type accept(io_context& ctx, endpoint_type& endpoint,error_code& ec);template<class OtherExecutor> socket_type_for<OtherExecutor> accept(const OtherExecutor& ex, endpoint_type& endpoint); template<class OtherExecutor> socket_type_for<OtherExecutor> accept(const OtherExecutor& ex, endpoint_type& endpoint, error_code& ec);
socklen_t endpoint_len = endpoint.capacity(); native_handle_type h = accept(native_handle(), endpoint.data(), &endpoint_len); if (h >= 0) endpoint.resize(endpoint_len);
template<class ExecutionContext> socket_type_for<typename ExecutionContext::executor_type> accept(ExecutionContext& ctx, endpoint_type& endpoint); template<class ExecutionContext> socket_type_for<typename ExecutionContext::executor_type> accept(ExecutionContext& ctx, endpoint_type& endpoint, error_code& ec);
template<class CompletionToken> DEDUCED async_accept(endpoint_type& endpoint, CompletionToken&& token);
template<class CompletionToken>DEDUCED async_accept(io_context& ctx, endpoint_type& endpoint,CompletionToken&& token);template<class OtherExecutor, class CompletionToken> DEDUCED async_accept(const OtherExecutor& ex, endpoint_type& endpoint, CompletionToken&& token);
socklen_t endpoint_len = endpoint.capacity(); native_handle_type h = accept(native_handle(), endpoint.data(), &endpoint_len); if (h >= 0) endpoint.resize(endpoint_len);
template<class ExecutionContext, class CompletionToken> DEDUCED async_accept(ExecutionContext& ctx, endpoint_type& endpoint, CompletionToken&& token);
async_accept(ctx.get_executor(), forward<CompletionToken>(token))
Update the class template basic_socket_streambuf
[socket.streambuf] as follows:
namespace std { namespace experimental { namespace net { inline namespace v1 { template<class Protocol, class Clock, class WaitTraits, class Executor = executor> class basic_socket_streambuf : public basic_streambuf<char> { public: // types: using executor_type = Executor; using protocol_type = Protocol; using endpoint_type = typename protocol_type::endpoint; using clock_type = Clock; using time_point = typename clock_type::time_point; using duration = typename clock_type::duration; using wait_traits_type = WaitTraits; // [socket.streambuf.cons], construct / copy / destroy: basic_socket_streambuf(); explicit basic_socket_streambuf(basic_stream_socket<protocol_type, executor_type> s); basic_socket_streambuf(const basic_socket_streambuf&) = delete; basic_socket_streambuf(basic_socket_streambuf&& rhs); virtual ~basic_socket_streambuf(); basic_socket_streambuf& operator=(const basic_socket_streambuf&) = delete; basic_socket_streambuf& operator=(basic_socket_streambuf&& rhs); // [socket.streambuf.members], members: basic_socket_streambuf* connect(const endpoint_type& e); template<class... Args> basic_socket_streambuf* connect(Args&&... ); basic_socket_streambuf* close(); basic_socket<protocol_type, executor_type>& socket(); error_code error() const; time_point expiry() const; void expires_at(const time_point& t); void expires_after(const duration& d); protected: // overridden virtual functions: virtual int_type underflow() override; virtual int_type pbackfail(int_type c = traits_type::eof()) override; virtual int_type overflow(int_type c = traits_type::eof()) override; virtual int sync() override; virtual streambuf* setbuf(char_type* s, streamsize n) override; private: basic_stream_socket<protocol_type, executor_type> socket_; // exposition only error_code ec_; // exposition only time_point expiry_; // exposition only }; } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
basic_socket_streambuf();
explicit basic_socket_streambuf(basic_stream_socket<protocol_type, executor_type> s);
basic_socket_streambuf(basic_socket_streambuf&& rhs);
virtual ~basic_socket_streambuf();
basic_socket_streambuf& operator=(basic_socket_streambuf&& rhs);
basic_socket_streambuf* connect(const endpoint_type& e);
template<class... Args> basic_socket_streambuf* connect(Args&&... args);
basic_socket_streambuf* close();
basic_socket<protocol_type, executor_type>& socket();
Update the class template basic_socket_iostream
[socket.iostream] as follows:
namespace std { namespace experimental { namespace net { inline namespace v1 { template<class Protocol, class Clock, class WaitTraits, class Executor = executor> class basic_socket_iostream : public basic_iostream<char> { public: // types: using executor_type = Executor; using protocol_type = Protocol; using endpoint_type = typename protocol_type::endpoint; using clock_type = Clock; using time_point = typename clock_type::time_point; using duration = typename clock_type::duration; using wait_traits_type = WaitTraits; // [socket.iostream.cons], construct / copy / destroy: basic_socket_iostream(); explicit basic_socket_iostream(basic_stream_socket<protocol_type, executor_type> s); basic_socket_iostream(const basic_socket_iostream&) = delete; basic_socket_iostream(basic_socket_iostream&& rhs); template<class... Args> explicit basic_socket_iostream(Args&&... args); basic_socket_iostream& operator=(const basic_socket_iostream&) = delete; basic_socket_iostream& operator=(basic_socket_iostream&& rhs); // [socket.iostream.members], members: template<class... Args> void connect(Args&&... args); void close(); basic_socket_streambuf<protocol_type, clock_type, wait_traits_type, executor_type>* rdbuf() const; basic_socket<protocol_type, executor_type>& socket(); error_code error() const; time_point expiry() const; void expires_at(const time_point& t); void expires_after(const duration& d); private: basic_socket_streambuf<protocol_type, clock_type, wait_traits_type, executor_type> sb_; // exposition only }; } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
basic_socket_iostream();
explicit basic_socket_iostream(basic_stream_socket<protocol_type, executor_type> s);
basic_socket_iostream(basic_socket_iostream&& rhs);
template<class... Args> explicit basic_socket_iostream(Args&&... args);
basic_socket_iostream& operator=(basic_socket_iostream&& rhs);
template<class... Args> void connect(Args&&... args);
void close();
basic_socket_streambuf<protocol_type, clock_type, wait_traits_type, executor_type>* rdbuf() const;
basic_socket<protocol_type, executor_type>& socket();
Update the connect
function [socket.algo.connect] as follows:
template<class Protocol, class Executor, class EndpointSequence> typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s, const EndpointSequence& endpoints); template<class Protocol, class Executor, class EndpointSequence> typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s, const EndpointSequence& endpoints, error_code& ec);
template<class Protocol, class Executor, class EndpointSequence, class ConnectCondition> typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s, const EndpointSequence& endpoints, ConnectCondition c); template<class Protocol, class Executor, class EndpointSequence, class ConnectCondition> typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s, const EndpointSequence& endpoints, ConnectCondition c, error_code& ec);
template<class Protocol, class Executor, class InputIterator> InputIterator connect(basic_socket<Protocol, Executor>& s, InputIterator first, InputIterator last); template<class Protocol, class Executor, class InputIterator> InputIterator connect(basic_socket<Protocol, Executor>& s, InputIterator first, InputIterator last, error_code& ec);
template<class Protocol, class Executor, class InputIterator, class ConnectCondition> InputIterator connect(basic_socket<Protocol, Executor>& s, InputIterator first, InputIterator last, ConnectCondition c); template<class Protocol, class Executor, class InputIterator, class ConnectCondition> InputIterator connect(basic_socket<Protocol, Executor>& s, InputIterator first, InputIterator last, ConnectCondition c, error_code& ec);
Update the async_connect
function [socket.algo.async.connect] as follows:
template<class Protocol, class Executor, class EndpointSequence, class CompletionToken> DEDUCED async_connect(basic_socket<Protocol, Executor>& s, const EndpointSequence& endpoints, CompletionToken&& token);
async_connect(s, endpoints, [](auto, auto){ return true; }, forward<CompletionToken>(token))
template<class Protocol, class Executor, class EndpointSequence, class ConnectCondition, class CompletionToken> DEDUCED async_connect(basic_socket<Protocol, Executor>& s, const EndpointSequence& endpoints, ConnectCondition c, CompletionToken&& token);
template<class Protocol, class Executor, class InputIterator, class CompletionToken> DEDUCED async_connect(basic_socket<Protocol, Executor>& s, InputIterator first, InputIterator last, CompletionToken&& token);
async_connect(s, first, last, [](auto, auto){ return true; }, forward<CompletionToken>(token))
template<class Protocol, class Executor, class InputIterator, class ConnectCondition, class CompletionToken> DEDUCED async_connect(basic_socket<Protocol, Executor>& s, InputIterator first, InputIterator last, ConnectCondition c, CompletionToken&& token);
Update the <experimental/internet>
synopsis [internet.synop] as follows:
[...]
template<class InternetProtocol, class Executor = executor> class basic_resolver;
Update the class template basic_resolver
[internet.resolver] as follows:
namespace std { namespace experimental { namespace net { inline namespace v1 { namespace ip { template<class InternetProtocol, class Executor = executor> class basic_resolver : public resolver_base { public: // types: using executor_type =io_context::executor_typeExecutor; using protocol_type = InternetProtocol; using endpoint_type = typename InternetProtocol::endpoint; using results_type = basic_resolver_results<InternetProtocol>; template<class OtherExecutor> using rebind_executor = basic_resolver<InternetProtocol, OtherExecutor>; // [internet.resolver.cons], construct / copy / destroy: explicit basic_resolver(io_context& ctxconst executor_type& ex); template<class ExecutionContext> explicit basic_resolver(ExecutionContext& ctx); basic_resolver(const basic_resolver&) = delete; basic_resolver(basic_resolver&& rhs) noexcept;
Update the basic_resolver
constructors [internet.resolver.cons] as follows:
explicit basic_resolver(io_context& ctxconst executor_type& ex);
template<class ExecutionContext> explicit basic_resolver(ExecutionContext& ctx);
basic_resolver(basic_resolver&& rhs) noexcept;
Update the ip::tcp
synopsis [internet.tcp] as follows:
namespace std { namespace experimental { namespace net { inline namespace v1 { namespace ip { class tcp { public: // types: using endpoint = basic_endpoint<tcp>; using resolver = basic_resolver<tcp>; using socket = basic_stream_socket<tcp>; template<class Executor> using socket_for = basic_stream_socket<tcp, Executor>; using acceptor = basic_socket_acceptor<tcp>; using iostream = basic_socket_iostream<tcp>; class no_delay; // static members: static constexpr tcp v4() noexcept; static constexpr tcp v6() noexcept; tcp() = delete; }; // [internet.tcp.comparisons], tcp comparisons: constexpr bool operator==(const tcp& a, const tcp& b) noexcept; constexpr bool operator!=(const tcp& a, const tcp& b) noexcept; } // namespace ip } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
Update the ip::udp
synopsis [internet.udp] as follows:
namespace std { namespace experimental { namespace net { inline namespace v1 { namespace ip { class udp { public: // types: using endpoint = basic_endpoint<udp>; using resolver = basic_resolver<udp>; using socket = basic_datagram_socket<udp>; template<class Executor> using socket_for = basic_datagram_socket<udp, Executor>; // static members: static constexpr udp v4() noexcept; static constexpr udp v6() noexcept; udp() = delete; }; // [internet.udp.comparisons], udp comparisons: constexpr bool operator==(const udp& a, const udp& b) noexcept; constexpr bool operator!=(const udp& a, const udp& b) noexcept; } // namespace ip } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
Changes in R3:
Note: changed sections in R3 are indicated by a pale blue background with a dark blue line on the right margin.socket_for
to AcceptableProtocol requirements, class ip::tcp
, and class ip::udp
.basic_socket_acceptor::rebind_socket_executor
to socket_type_for
.Changes in R2:
rebind_executor
to be a template type alias.basic_socket_acceptor::socket_type
.basic_socket_acceptor::rebind_socket_executor
.basic_socket_acceptor::accept
and basic_socket_acceptor::async_accept
overloads that take an executor (or execution context) to use the new rebind_socket_executor
type alias.Changes in R1: