gcc/libstdc++-v3/include/experimental/socket
Jonathan Wakely 98f29f5638 libstdc++: Fix type-erasure in experimental::net::executor (PR 94203)
The _Tgt and _TgtImpl types that implement type-erasure didn't agree on
the virtual interface, so failed as soon as they were instantiated. With
Clang they failed even sooner. The interface was also dependent on
whether RTTI was enabled or not.

This patch fixes the broken virtual functions and makes the type work
without RTTI, by using a pointer to a specialization of a function
template (similar to the approaches in std::function and std::any).

The changes to the virtual functions would be an ABI change, except that
the previous code didn't even compile if instantiated. This is
experimental TS material anyway.

	PR libstdc++/94203
	* include/experimental/executor (executor::executor(Executor)): Call
	make_shared directly instead of _M_create. Create _Tgt1 object.
	(executor::executor(allocator_arg_t, const ProtoAlloc&, Executor)):
	Call allocate_shared directly instead of _M_create. Create _Tgt2
	object.
	(executor::target_type): Add cast needed for new _Tgt interface.
	(executor::target): Define when RTTI is disabled. Use _Tgt::_M_func.
	(executor::_Tgt): Define the same interface whether RTTI is enabled or
	not.
	(executor::_Tgt::target_type, executor::_Tgt::target): Do not use
	std::type_info in the interface.
	(executor::_Tgt::_M_func): Add data member.
	(executor::_TgtImpl): Replace with _Tgt1 and _Tgt2 class templates.
	(executor::_Tgt1::_S_func): Define function to access target without
	depending on RTTI.
	(executor::_M_create): Remove.
	(operator==, operator!=): Simplify comparisons for executor.
	* include/experimental/socket (is_error_code_enum<socket_errc>):
	Define specialization before use.
	* testsuite/experimental/net/executor/1.cc: New test.
2020-03-18 00:23:39 +00:00

2624 lines
74 KiB
C++

// <experimental/socket> -*- C++ -*-
// Copyright (C) 2015-2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/** @file experimental/socket
* This is a TS C++ Library header.
* @ingroup networking-ts
*/
#ifndef _GLIBCXX_EXPERIMENTAL_SOCKET
#define _GLIBCXX_EXPERIMENTAL_SOCKET
#pragma GCC system_header
#if __cplusplus >= 201402L
#include <experimental/netfwd>
#include <experimental/buffer>
#include <experimental/io_context>
#include <experimental/bits/net.h>
#include <streambuf>
#include <istream>
#include <bits/unique_ptr.h>
#if _GLIBCXX_HAVE_UNISTD_H
# include <unistd.h>
# ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
# include <sys/socket.h> // socket etc
# endif
# ifdef _GLIBCXX_HAVE_SYS_IOCTL_H
# include <sys/ioctl.h> // ioctl
# endif
# ifdef _GLIBCXX_HAVE_SYS_UIO_H
# include <sys/uio.h> // iovec
# endif
# ifdef _GLIBCXX_HAVE_POLL_H
# include <poll.h> // poll, pollfd, POLLIN, POLLOUT, POLLERR
# endif
# ifdef _GLIBCXX_HAVE_FCNTL_H
# include <fcntl.h> // fcntl, F_GETFL, F_SETFL
# endif
#endif
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
namespace experimental
{
namespace net
{
inline namespace v1
{
/** @addtogroup networking-ts
* @{
*/
enum class socket_errc { // TODO decide values
already_open = 3,
not_found = 4
};
} // namespace v1
} // namespace net
} // namespace experimental
template<>
struct is_error_code_enum<experimental::net::v1::socket_errc>
: public true_type {};
namespace experimental
{
namespace net
{
inline namespace v1
{
const error_category& socket_category() noexcept
{
struct __cat : error_category
{
const char* name() const noexcept { return "socket"; }
std::string message(int __e) const
{
if (__e == (int)socket_errc::already_open)
return "already open";
else if (__e == (int)socket_errc::not_found)
return "endpoint not found";
return "socket error";
}
virtual void __message(int) { } // TODO dual ABI XXX
};
static __cat __c;
return __c;
}
inline error_code
make_error_code(socket_errc __e) noexcept
{ return error_code(static_cast<int>(__e), socket_category()); }
inline error_condition
make_error_condition(socket_errc __e) noexcept
{ return error_condition(static_cast<int>(__e), socket_category()); }
template<typename _Tp, typename = __void_t<>>
struct __is_endpoint_impl : false_type
{ };
// Check Endpoint requirements.
template<typename _Tp>
auto
__endpoint_reqs(const _Tp* __a = 0)
-> enable_if_t<__and_<
is_default_constructible<_Tp>,
__is_value_constructible<_Tp>,
is_same<decltype(__a->__protocol()), typename _Tp::protocol_type>
>::value,
__void_t< typename _Tp::protocol_type::endpoint >>;
template<typename _Tp>
struct __is_endpoint_impl<_Tp, decltype(__endpoint_reqs<_Tp>())>
: true_type
{ };
template<typename _Tp>
struct __is_endpoint : __is_endpoint_impl<_Tp>
{ };
// TODO Endpoint reqs for extensible implementations
// TODO _Protocol reqs
// TODO AcceptableProtocol reqs
// TODO GettableSocket reqs
// TODO SettableSocket reqs
// TODO BooleanSocketOption reqs
// TODO IntegerSocketOption reqs
// TODO _IoControlCommand reqs
// TODO _ConnectCondition reqs
/** @brief Sockets
* @{
*/
class socket_base
{
public:
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
struct broadcast : __sockopt_crtp<broadcast, bool>
{
using __sockopt_crtp::__sockopt_crtp;
static const int _S_level = SOL_SOCKET;
static const int _S_name = SO_BROADCAST;
};
struct debug : __sockopt_crtp<debug, bool>
{
using __sockopt_crtp::__sockopt_crtp;
static const int _S_level = SOL_SOCKET;
static const int _S_name = SO_DEBUG;
};
struct do_not_route : __sockopt_crtp<do_not_route, bool>
{
using __sockopt_crtp::__sockopt_crtp;
static const int _S_level = SOL_SOCKET;
static const int _S_name = SO_DONTROUTE;
};
struct keep_alive : __sockopt_crtp<keep_alive, bool>
{
using __sockopt_crtp::__sockopt_crtp;
static const int _S_level = SOL_SOCKET;
static const int _S_name = SO_KEEPALIVE;
};
struct linger : __sockopt_crtp<linger, ::linger>
{
using __sockopt_crtp::__sockopt_crtp;
linger() noexcept = default;
linger(bool __e, chrono::seconds __t) noexcept
{
enabled(__e);
timeout(__t);
}
bool
enabled() const noexcept
{ return _M_value.l_onoff != 0; }
void
enabled(bool __e) noexcept
{ _M_value.l_onoff = int(__e); }
chrono::seconds
timeout() const noexcept
{ return chrono::seconds(_M_value.l_linger); }
void
timeout(chrono::seconds __t) noexcept
{ _M_value.l_linger = __t.count(); }
static const int _S_level = SOL_SOCKET;
static const int _S_name = SO_LINGER;
};
struct out_of_band_inline : __sockopt_crtp<out_of_band_inline, bool>
{
using __sockopt_crtp::__sockopt_crtp;
static const int _S_level = SOL_SOCKET;
static const int _S_name = SO_OOBINLINE;
};
struct receive_buffer_size : __sockopt_crtp<receive_buffer_size>
{
using __sockopt_crtp::__sockopt_crtp;
static const int _S_level = SOL_SOCKET;
static const int _S_name = SO_RCVBUF;
};
struct receive_low_watermark : __sockopt_crtp<receive_low_watermark>
{
using __sockopt_crtp::__sockopt_crtp;
static const int _S_level = SOL_SOCKET;
static const int _S_name = SO_RCVLOWAT;
};
struct reuse_address : __sockopt_crtp<reuse_address, bool>
{
using __sockopt_crtp::__sockopt_crtp;
static const int _S_level = SOL_SOCKET;
static const int _S_name = SO_REUSEADDR;
};
struct send_buffer_size : __sockopt_crtp<send_buffer_size>
{
using __sockopt_crtp::__sockopt_crtp;
static const int _S_level = SOL_SOCKET;
static const int _S_name = SO_SNDBUF;
};
struct send_low_watermark : __sockopt_crtp<send_low_watermark>
{
using __sockopt_crtp::__sockopt_crtp;
static const int _S_level = SOL_SOCKET;
static const int _S_name = SO_SNDLOWAT;
};
enum shutdown_type : int
{
__shutdown_receive = SHUT_RD,
__shutdown_send = SHUT_WR,
__shutdown_both = SHUT_RDWR
};
static constexpr shutdown_type shutdown_receive = __shutdown_receive;
static constexpr shutdown_type shutdown_send = __shutdown_send;
static constexpr shutdown_type shutdown_both = __shutdown_both;
#ifdef _GLIBCXX_HAVE_POLL_H
enum wait_type : int
{
__wait_read = POLLIN,
__wait_write = POLLOUT,
__wait_error = POLLERR
};
static constexpr wait_type wait_read = __wait_read;
static constexpr wait_type wait_write = __wait_write;
static constexpr wait_type wait_error = __wait_error;
#endif
enum message_flags : int
{
__message_peek = MSG_PEEK,
__message_oob = MSG_OOB,
__message_dontroute = MSG_DONTROUTE
};
static constexpr message_flags message_peek = __message_peek;
static constexpr message_flags message_out_of_band = __message_oob;
static constexpr message_flags message_do_not_route = __message_dontroute;
static const int max_listen_connections = SOMAXCONN;
#endif
protected:
socket_base() = default;
~socket_base() = default;
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
struct __msg_hdr : ::msghdr
{
#ifdef IOV_MAX
using __iovec_array = array<::iovec, IOV_MAX>;
#elif _GLIBCXX_HAVE_UNISTD_H
struct __iovec_array
{
__iovec_array() : _M_ptr(new ::iovec[size()]) { }
::iovec& operator[](size_t __n) noexcept { return _M_ptr[__n]; }
::iovec* data() noexcept { return _M_ptr.get(); }
static size_t size()
{
static const size_t __iov_max = ::sysconf(_SC_IOV_MAX);
return __iov_max;
}
private:
unique_ptr<::iovec[]> _M_ptr;
};
#else
using __iovec_array = array<::iovec, 16>;
#endif
__iovec_array _M_iov;
template<typename _BufferSequence>
explicit
__msg_hdr(const _BufferSequence& __buffers)
: msghdr()
{
auto __buf = net::buffer_sequence_begin(__buffers);
const auto __bufend = net::buffer_sequence_end(__buffers);
size_t __len = 0;
while (__buf != __bufend && __len != _M_iov.size())
{
_M_iov[__len].iov_base = (void*)__buf->data();
_M_iov[__len].iov_len = __buf->size();
++__buf;
++__len;
}
this->msg_iovlen = __len;
this->msg_iov = _M_iov.data();
}
template<typename _BufferSequence, typename _Endpoint>
__msg_hdr(const _BufferSequence& __buffers, const _Endpoint& __ep)
: __msg_hdr(__buffers)
{
this->msg_name = __ep.data();
this->msg_namelen = __ep.size();
}
};
#endif
};
constexpr socket_base::message_flags
operator&(socket_base::message_flags __f1, socket_base::message_flags __f2)
{ return socket_base::message_flags( int(__f1) & int(__f2) ); }
constexpr socket_base::message_flags
operator|(socket_base::message_flags __f1, socket_base::message_flags __f2)
{ return socket_base::message_flags( int(__f1) | int(__f2) ); }
constexpr socket_base::message_flags
operator^(socket_base::message_flags __f1, socket_base::message_flags __f2)
{ return socket_base::message_flags( int(__f1) ^ int(__f2) ); }
constexpr socket_base::message_flags
operator~(socket_base::message_flags __f)
{ return socket_base::message_flags( ~int(__f) ); }
inline socket_base::message_flags&
operator&=(socket_base::message_flags& __f1, socket_base::message_flags __f2)
{ return __f1 = (__f1 & __f2); }
inline socket_base::message_flags&
operator|=(socket_base::message_flags& __f1, socket_base::message_flags __f2)
{ return __f1 = (__f1 | __f2); }
inline socket_base::message_flags&
operator^=(socket_base::message_flags& __f1, socket_base::message_flags __f2)
{ return __f1 = (__f1 ^ __f2); }
#if _GLIBCXX_HAVE_UNISTD_H
class __socket_impl
{
protected:
using executor_type = io_context::executor_type;
using native_handle_type = int;
explicit
__socket_impl(io_context& __ctx) : _M_ctx(std::addressof(__ctx)) { }
__socket_impl(__socket_impl&& __rhs)
: _M_ctx(__rhs._M_ctx),
_M_sockfd(std::exchange(__rhs._M_sockfd, -1)),
_M_bits(std::exchange(__rhs._M_bits, {}))
{ }
__socket_impl&
operator=(__socket_impl&& __rhs)
{
_M_ctx = __rhs._M_ctx;
_M_sockfd = std::exchange(__rhs._M_sockfd, -1);
_M_bits = std::exchange(__rhs._M_bits, {});
return *this;
}
~__socket_impl() = default;
__socket_impl(const __socket_impl&) = delete;
__socket_impl& operator=(const __socket_impl&) = delete;
executor_type get_executor() noexcept { return _M_ctx->get_executor(); }
native_handle_type native_handle() noexcept { return _M_sockfd; }
bool is_open() const noexcept { return _M_sockfd != -1; }
void
close(error_code& __ec)
{
if (is_open())
{
cancel(__ec);
if (!__ec)
{
if (::close(_M_sockfd) == -1)
__ec.assign(errno, generic_category());
else
{
get_executor().context()._M_remove_fd(_M_sockfd);
_M_sockfd = -1;
}
}
}
}
void cancel(error_code& __ec) { _M_ctx->cancel(_M_sockfd, __ec); }
void
non_blocking(bool __mode, error_code&)
{ _M_bits.non_blocking = __mode; }
bool non_blocking() const { return _M_bits.non_blocking; }
void
native_non_blocking(bool __mode, error_code& __ec)
{
#ifdef _GLIBCXX_HAVE_FCNTL_H
int __flags = ::fcntl(_M_sockfd, F_GETFL, 0);
if (__flags >= 0)
{
if (__mode)
__flags |= O_NONBLOCK;
else
__flags &= ~O_NONBLOCK;
__flags = ::fcntl(_M_sockfd, F_SETFL, __flags);
}
if (__flags == -1)
__ec.assign(errno, generic_category());
else
{
__ec.clear();
_M_bits.native_non_blocking = __mode;
}
#else
__ec = std::make_error_code(std::errc::not_supported);
#endif
}
bool
native_non_blocking() const
{
#ifdef _GLIBCXX_HAVE_FCNTL_H
if (_M_bits.native_non_blocking == -1)
{
const int __flags = ::fcntl(_M_sockfd, F_GETFL, 0);
if (__flags == -1)
return 0;
_M_bits.native_non_blocking = __flags & O_NONBLOCK;
}
return _M_bits.native_non_blocking;
#else
return false;
#endif
}
io_context* _M_ctx;
int _M_sockfd{-1};
struct {
unsigned non_blocking : 1;
mutable signed native_non_blocking : 2;
unsigned enable_connection_aborted : 1;
} _M_bits{};
};
template<typename _Protocol>
class __basic_socket_impl : public __socket_impl
{
using __base = __socket_impl;
protected:
using protocol_type = _Protocol;
using endpoint_type = typename protocol_type::endpoint;
explicit
__basic_socket_impl(io_context& __ctx) : __base(__ctx) { }
__basic_socket_impl(__basic_socket_impl&&) = default;
template<typename _OtherProtocol>
__basic_socket_impl(__basic_socket_impl<_OtherProtocol>&& __rhs)
: __base(std::move(__rhs)), _M_protocol(std::move(__rhs._M_protocol))
{ }
__basic_socket_impl&
operator=(__basic_socket_impl&& __rhs)
{
if (this == std::addressof(__rhs))
return *this;
_M_close();
__base::operator=(std::move(__rhs));
return *this;
}
~__basic_socket_impl() { _M_close(); }
__basic_socket_impl(const __basic_socket_impl&) = delete;
__basic_socket_impl& operator=(const __basic_socket_impl&) = delete;
void
open(const protocol_type& __protocol, error_code& __ec)
{
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
if (is_open())
__ec = socket_errc::already_open;
else
{
_M_protocol = __protocol;
_M_sockfd = ::socket(__protocol.family(), __protocol.type(),
__protocol.protocol());
if (is_open())
{
get_executor().context()._M_add_fd(_M_sockfd);
__ec.clear();
}
else
__ec.assign(errno, std::generic_category());
}
#else
__ec = std::make_error_code(errc::operation_not_supported);
#endif
}
void
assign(const protocol_type& __protocol,
const native_handle_type& __native_socket,
error_code& __ec)
{
if (is_open())
__ec = socket_errc::already_open;
else
{
_M_protocol = __protocol;
_M_bits.native_non_blocking = -1;
_M_sockfd = __native_socket;
if (is_open())
{
get_executor().context()._M_add_fd(_M_sockfd);
__ec.clear();
}
else
__ec.assign(errno, std::generic_category());
}
}
template<typename _SettableSocketOption>
void
set_option(const _SettableSocketOption& __option, error_code& __ec)
{
int __result = ::setsockopt(_M_sockfd, __option.level(_M_protocol),
__option.name(_M_protocol),
__option.data(_M_protocol),
__option.size(_M_protocol));
if (__result == -1)
__ec.assign(errno, generic_category());
else
__ec.clear();
}
template<typename _GettableSocketOption>
void
get_option(_GettableSocketOption& __option, error_code& __ec) const
{
int __result = ::getsockopt(_M_sockfd, __option.level(_M_protocol),
__option.name(_M_protocol),
__option.data(_M_protocol),
__option.size(_M_protocol));
if (__result == -1)
__ec.assign(errno, generic_category());
else
__ec.clear();
}
template<typename _IoControlCommand>
void
io_control(_IoControlCommand& __command, error_code& __ec)
{
#ifdef _GLIBCXX_HAVE_SYS_IOCTL_H
int __result = ::ioctl(_M_sockfd, __command.name(),
__command.data());
if (__result == -1)
__ec.assign(errno, generic_category());
else
__ec.clear();
#else
__ec = std::make_error_code(std::errc::not_supported);
#endif
}
endpoint_type
local_endpoint(error_code& __ec) const
{
endpoint_type __endpoint;
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
socklen_t __endpoint_len = __endpoint.capacity();
if (::getsockname(_M_sockfd, (sockaddr*)__endpoint.data(),
&__endpoint_len) == -1)
{
__ec.assign(errno, generic_category());
return endpoint_type{};
}
__ec.clear();
__endpoint.resize(__endpoint_len);
#else
__ec = std::make_error_code(errc::operation_not_supported);
#endif
return __endpoint;
}
void
bind(const endpoint_type& __endpoint, error_code& __ec)
{
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
if (::bind(_M_sockfd, (sockaddr*)__endpoint.data(), __endpoint.size())
== -1)
__ec.assign(errno, generic_category());
else
__ec.clear();
#else
__ec = std::make_error_code(errc::operation_not_supported);
#endif
}
_Protocol _M_protocol{ endpoint_type{}.protocol() };
private:
void
_M_close()
{
if (is_open())
{
error_code __ec;
cancel(__ec);
set_option(socket_base::linger{false, chrono::seconds{}}, __ec);
::close(_M_sockfd);
}
}
};
template<typename _Protocol>
class basic_socket
: public socket_base, private __basic_socket_impl<_Protocol>
{
using __base = __basic_socket_impl<_Protocol>;
public:
// types:
typedef io_context::executor_type executor_type;
typedef int native_handle_type;
typedef _Protocol protocol_type;
typedef typename protocol_type::endpoint endpoint_type;
// basic_socket operations:
executor_type get_executor() noexcept { return __base::get_executor(); }
native_handle_type
native_handle() noexcept { return __base::native_handle(); }
void
open(const protocol_type& __protocol = protocol_type())
{ open(__protocol, __throw_on_error{"basic_socket::open"}); }
void
open(const protocol_type& __protocol, error_code& __ec)
{ __base::open(__protocol, __ec); }
void
assign(const protocol_type& __protocol,
const native_handle_type& __native_socket)
{
assign(__protocol, __native_socket,
__throw_on_error{"basic_socket::assign"});
}
void
assign(const protocol_type& __protocol,
const native_handle_type& __native_socket,
error_code& __ec)
{ __base::assign(__protocol, __native_socket, __ec); }
bool is_open() const noexcept { return __base::is_open(); }
void close() { close(__throw_on_error{"basic_socket::close"}); }
void close(error_code& __ec) { __base::close(); }
void cancel() { cancel(__throw_on_error{"basic_socket::cancel"}); }
void cancel(error_code& __ec) { __base::cancel(__ec); }
template<typename _SettableSocketOption>
void
set_option(const _SettableSocketOption& __option)
{ set_option(__option, __throw_on_error{"basic_socket::set_option"}); }
template<typename _SettableSocketOption>
void
set_option(const _SettableSocketOption& __option, error_code& __ec)
{ __base::set_option(__option, __ec); }
template<typename _GettableSocketOption>
void
get_option(_GettableSocketOption& __option) const
{ get_option(__option, __throw_on_error{"basic_socket::get_option"}); }
template<typename _GettableSocketOption>
void
get_option(_GettableSocketOption& __option, error_code& __ec) const
{ __base::get_option(__option, __ec); }
template<typename _IoControlCommand>
void
io_control(_IoControlCommand& __command)
{
io_control(__command, __throw_on_error{"basic_socket::io_control"});
}
template<typename _IoControlCommand>
void
io_control(_IoControlCommand& __command, error_code& __ec)
{ __base::io_control(__command, __ec); }
void
non_blocking(bool __mode)
{ non_blocking(__mode, __throw_on_error{"basic_socket::non_blocking"}); }
void
non_blocking(bool __mode, error_code& __ec)
{ __base::non_blocking(__mode, __ec); }
bool non_blocking() const { return __base::non_blocking(); }
void
native_non_blocking(bool __mode)
{
native_non_blocking(__mode, __throw_on_error{
"basic_socket::native_non_blocking"});
}
void
native_non_blocking(bool __mode, error_code& __ec)
{ __base::native_non_blocking(__mode, __ec); }
bool
native_non_blocking() const
{ return __base::native_non_blocking(); }
bool at_mark() const
{ return at_mark(__throw_on_error{"basic_socket::at_mark"}); }
bool
at_mark(error_code& __ec) const
{
#ifdef _GLIBCXX_HAVE_SOCKATMARK
const int __result = ::sockatmark(native_handle());
if (__result == -1)
{
__ec.assign(errno, generic_category());
return false;
}
__ec.clear();
return (bool)__result;
#else
__ec = std::make_error_code(errc::operation_not_supported);
return false;
#endif
}
size_t
available() const
{ return available(__throw_on_error{"basic_socket::available"}); }
size_t
available(error_code& __ec) const
{
if (!is_open())
{
__ec = std::make_error_code(errc::bad_file_descriptor);
return 0;
}
#if defined _GLIBCXX_HAVE_SYS_IOCTL_H && defined FIONREAD
int __avail = 0;
if (::ioctl(this->_M_sockfd, FIONREAD, &__avail) == -1)
{
__ec.assign(errno, generic_category());
return 0;
}
__ec.clear();
return __avail;
#else
return 0;
#endif
}
void
bind(const endpoint_type& __endpoint)
{ return bind(__endpoint, __throw_on_error{"basic_socket::bind"}); }
void
bind(const endpoint_type& __endpoint, error_code& __ec)
{ __base::bind(__endpoint, __ec); }
void shutdown(shutdown_type __what)
{ return shutdown(__what, __throw_on_error{"basic_socket::shutdown"}); }
void
shutdown(shutdown_type __what, error_code& __ec)
{
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
if (::shutdown(native_handle(), static_cast<int>(__what)) == -1)
__ec.assign(errno, generic_category());
else
__ec.clear();
#else
__ec = std::make_error_code(errc::operation_not_supported);
#endif
}
endpoint_type
local_endpoint() const
{
return local_endpoint(
__throw_on_error{"basic_socket::local_endpoint"});
}
endpoint_type
local_endpoint(error_code& __ec) const
{ return __base::local_endpoint(__ec); }
endpoint_type
remote_endpoint() const
{
return remote_endpoint(
__throw_on_error{"basic_socket::remote_endpoint"});
}
endpoint_type
remote_endpoint(error_code& __ec) const
{
endpoint_type __endpoint;
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
socklen_t __endpoint_len = __endpoint.capacity();
if (::getpeername(this->_M_sockfd, (sockaddr*)__endpoint.data(),
&__endpoint_len)
== -1)
{
__ec.assign(errno, generic_category());
return endpoint_type{};
}
__ec.clear();
__endpoint.resize(__endpoint_len);
#else
__ec = std::make_error_code(errc::operation_not_supported);
#endif
return __endpoint;
}
void
connect(const endpoint_type& __endpoint)
{
return connect(__endpoint, __throw_on_error{"basic_socket::connect"});
}
void
connect(const endpoint_type& __endpoint, error_code& __ec)
{
if (!is_open())
{
open(__endpoint.protocol(), __ec);
if (__ec)
return;
}
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
if (::connect(native_handle(), (const sockaddr*)__endpoint.data(),
__endpoint.size()) == -1)
__ec.assign(errno, generic_category());
else
__ec.clear();
#else
__ec = std::make_error_code(errc::operation_not_supported);
#endif
}
template<typename _CompletionToken>
__deduced_t<_CompletionToken, void(error_code)>
async_connect(const endpoint_type& __endpoint,
_CompletionToken&& __token)
{
async_completion<_CompletionToken, void(error_code)> __init{__token};
if (!is_open())
{
error_code __ec;
open(__endpoint.protocol(), __ec);
if (__ec)
{
auto __ex = net::get_associated_executor(
__init.completion_handler, get_executor());
auto __a = get_associated_allocator(
__init.completion_handler, std::allocator<void>());
__ex.post(
[__h=std::move(__init.completion_handler), __ec]
() mutable
{ __h(__ec); }, __a);
return __init.result.get();
}
}
get_executor().context().async_wait( native_handle(),
socket_base::wait_read,
[__h = std::move(__init.completion_handler),
__ep = std::move(__endpoint),
__fd = native_handle()]
(error_code __ec) mutable {
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
if (!__ec && ::connect(__fd, (const sockaddr*)__ep.data(),
__ep.size()) == -1)
__ec.assign(errno, generic_category());
#else
__ec = std::make_error_code(errc::operation_not_supported);
#endif
__h(__ec);
});
return __init.result.get();
}
void
wait(wait_type __w)
{ return wait(__w, __throw_on_error{"basic_socket::wait"}); }
void
wait(wait_type __w, error_code& __ec)
{
#ifdef _GLIBCXX_HAVE_POLL_H
::pollfd __fd;
__fd.fd = native_handle();
__fd.events = static_cast<int>(__w);
int __res = ::poll(&__fd, 1, -1);
if (__res == -1)
__ec.assign(errno, generic_category());
else
__ec.clear();
#else
__ec = std::make_error_code(errc::operation_not_supported);
#endif
}
template<typename _CompletionToken>
__deduced_t<_CompletionToken, void(error_code)>
async_wait(wait_type __w, _CompletionToken&& __token)
{
async_completion<_CompletionToken, void(error_code)> __init{__token};
get_executor().context().async_wait( native_handle(),
static_cast<int>(__w),
[__h = std::move(__init.completion_handler)]
(error_code __ec) mutable {
__h(__ec);
});
return __init.result.get();
}
protected:
// construct / copy / destroy:
using __base::__base;
explicit
basic_socket(io_context& __ctx) : __base(__ctx) { }
basic_socket(io_context& __ctx, const protocol_type& __protocol)
: __base(__ctx)
{ open(__protocol); }
basic_socket(io_context& __ctx, const endpoint_type& __endpoint)
: basic_socket(std::addressof(__ctx), __endpoint.protocol())
{ bind(__endpoint); }
basic_socket(io_context& __ctx, const protocol_type& __protocol,
const native_handle_type& __native_socket)
: __base(__ctx)
{ assign(__protocol, __native_socket); }
basic_socket(const basic_socket&) = delete;
basic_socket(basic_socket&& __rhs) = default;
template<typename _OtherProtocol, typename _Requires
= _Require<is_convertible<_OtherProtocol, _Protocol>>>
basic_socket(basic_socket<_OtherProtocol>&& __rhs)
: __base(std::move(__rhs)) { }
~basic_socket() = default;
basic_socket& operator=(const basic_socket&) = delete;
basic_socket& operator=(basic_socket&& __rhs) = default;
template<typename _OtherProtocol>
enable_if_t<is_convertible<_OtherProtocol, _Protocol>::value,
basic_socket&>
operator=(basic_socket<_OtherProtocol>&& __rhs)
{ return *this = basic_socket{std::move(__rhs)}; }
};
template<typename _Protocol>
class basic_datagram_socket : public basic_socket<_Protocol>
{
using __base = basic_socket<_Protocol>;
public:
// types:
typedef int native_handle_type;
typedef _Protocol protocol_type;
typedef typename protocol_type::endpoint endpoint_type;
// construct / copy / destroy:
explicit
basic_datagram_socket(io_context& __ctx) : __base(__ctx) { }
basic_datagram_socket(io_context& __ctx, const protocol_type& __protocol)
: __base(__ctx, __protocol) { }
basic_datagram_socket(io_context& __ctx, const endpoint_type& __endpoint)
: __base(__ctx, __endpoint) { }
basic_datagram_socket(io_context& __ctx, const protocol_type& __protocol,
const native_handle_type& __native_socket)
: __base(__ctx, __protocol, __native_socket) { }
basic_datagram_socket(const basic_datagram_socket&) = delete;
basic_datagram_socket(basic_datagram_socket&& __rhs) = default;
template<typename _OtherProtocol, typename _Requires
= _Require<is_convertible<_OtherProtocol, _Protocol>>>
basic_datagram_socket(basic_datagram_socket<_OtherProtocol>&& __rhs)
: __base(std::move(__rhs)) { }
~basic_datagram_socket() = default;
basic_datagram_socket& operator=(const basic_datagram_socket&) = delete;
basic_datagram_socket& operator=(basic_datagram_socket&& __rhs) = default;
template<typename _OtherProtocol>
enable_if_t<is_convertible<_OtherProtocol, _Protocol>::value,
basic_datagram_socket&>
operator=(basic_datagram_socket<_OtherProtocol>&& __rhs)
{
__base::operator=(std::move(__rhs));
return *this;
}
// basic_datagram_socket operations:
template<typename _MutableBufferSequence>
size_t
receive(const _MutableBufferSequence& __buffers)
{
return receive(__buffers, socket_base::message_flags(),
__throw_on_error{"basic_datagram_socket::receive"});
}
template<typename _MutableBufferSequence>
size_t
receive(const _MutableBufferSequence& __buffers, error_code& __ec)
{ return receive(__buffers, socket_base::message_flags(), __ec); }
template<typename _MutableBufferSequence>
size_t
receive(const _MutableBufferSequence& __buffers,
socket_base::message_flags __flags)
{
return receive(__buffers, __flags,
__throw_on_error{"basic_datagram_socket::receive"});
}
template<typename _MutableBufferSequence>
size_t
receive(const _MutableBufferSequence& __buffers,
socket_base::message_flags __flags, error_code& __ec)
{
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
socket_base::__msg_hdr __msg(__buffers);
ssize_t __result = ::recvmsg(this->native_handle(), &__msg,
static_cast<int>(__flags));
if (__result == -1)
{
__ec.assign(errno, generic_category());
return 0;
}
__ec.clear();
return __result;
#else
__ec = std::make_error_code(errc::operation_not_supported);
return 0;
#endif
}
template<typename _MutableBufferSequence, typename _CompletionToken>
__deduced_t<_CompletionToken, void(error_code, size_t)>
async_receive(const _MutableBufferSequence& __buffers,
_CompletionToken&& __token)
{
return async_receive(__buffers, socket_base::message_flags(),
std::forward<_CompletionToken>(__token));
}
template<typename _MutableBufferSequence, typename _CompletionToken>
__deduced_t<_CompletionToken, void(error_code, size_t)>
async_receive(const _MutableBufferSequence& __buffers,
socket_base::message_flags __flags,
_CompletionToken&& __token)
{
async_completion<_CompletionToken, void(error_code, size_t)>
__init{__token};
this->get_executor().context().async_wait(this->native_handle(),
socket_base::wait_read,
[__h = std::move(__init.completion_handler),
&__buffers, __flags = static_cast<int>(__flags),
__fd = this->native_handle()]
(error_code __ec) mutable {
if (__ec)
{
__h(__ec);
return;
}
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
socket_base::__msg_hdr __msg(__buffers);
ssize_t __result = ::recvmsg(__fd, &__msg, __flags);
if (__result == -1)
{
__ec.assign(errno, generic_category());
__result = 0;
}
else
__ec.clear();
__h(__ec, __result);
#else
__h(std::make_error_code(errc::operation_not_supported), 0);
#endif
});
return __init.result.get();
}
template<typename _MutableBufferSequence>
size_t
receive_from(const _MutableBufferSequence& __buffers,
endpoint_type& __sender)
{
return receive_from(__buffers, __sender,
socket_base::message_flags(),
__throw_on_error{
"basic_datagram_socket::receive_from"});
}
template<typename _MutableBufferSequence>
size_t
receive_from(const _MutableBufferSequence& __buffers,
endpoint_type& __sender, error_code& __ec)
{
return receive_from(__buffers, __sender,
socket_base::message_flags(), __ec);
}
template<typename _MutableBufferSequence>
size_t
receive_from(const _MutableBufferSequence& __buffers,
endpoint_type& __sender,
socket_base::message_flags __flags)
{
return receive_from(__buffers, __sender, __flags,
__throw_on_error{
"basic_datagram_socket::receive_from"});
}
template<typename _MutableBufferSequence>
size_t
receive_from(const _MutableBufferSequence& __buffers,
endpoint_type& __sender,
socket_base::message_flags __flags,
error_code& __ec)
{
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
socket_base::__msg_hdr __msg(__buffers, __sender);
ssize_t __result = ::recvmsg(this->native_handle(), &__msg,
static_cast<int>(__flags));
if (__result == -1)
{
__ec.assign(errno, generic_category());
return 0;
}
__ec.clear();
__sender.resize(__msg.msg_namelen);
return __result;
#else
__ec = std::make_error_code(errc::operation_not_supported);
return 0;
#endif
}
template<typename _MutableBufferSequence, typename _CompletionToken>
__deduced_t<_CompletionToken, void(error_code, size_t)>
async_receive_from(const _MutableBufferSequence& __buffers,
endpoint_type& __sender,
_CompletionToken&& __token)
{
return async_receive_from(__buffers, __sender,
socket_base::message_flags(),
std::forward<_CompletionToken>(__token));
}
template<typename _MutableBufferSequence, typename _CompletionToken>
__deduced_t<_CompletionToken, void(error_code, size_t)>
async_receive_from(const _MutableBufferSequence& __buffers,
endpoint_type& __sender,
socket_base::message_flags __flags,
_CompletionToken&& __token)
{
async_completion<_CompletionToken, void(error_code, size_t)>
__init{__token};
this->get_executor().context().async_wait( this->native_handle(),
socket_base::wait_read,
[__h = std::move(__init.completion_handler),
&__buffers, __flags = static_cast<int>(__flags),
__sender = std::move(__sender),
__fd = this->native_handle()]
(error_code __ec) mutable {
if (__ec)
{
__h(__ec);
return;
}
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
socket_base::__msg_hdr __msg(__buffers, __sender);
ssize_t __result = ::recvmsg(__fd, &__msg, __flags);
if (__result == -1)
{
__ec.assign(errno, generic_category());
__result = 0;
}
else
{
__ec.clear();
__sender.resize(__msg.msg_namelen);
}
__h(__ec, __result);
#else
__h(std::make_error_code(errc::operation_not_supported), 0);
#endif
});
return __init.result.get();
}
template<typename _ConstBufferSequence>
size_t
send(const _ConstBufferSequence& __buffers)
{
return send(__buffers, socket_base::message_flags(),
__throw_on_error{"basic_datagram_socket::send"});
}
template<typename _ConstBufferSequence>
size_t
send(const _ConstBufferSequence& __buffers, error_code& __ec)
{ return send(__buffers, socket_base::message_flags(), __ec); }
template<typename _ConstBufferSequence>
size_t
send(const _ConstBufferSequence& __buffers,
socket_base::message_flags __flags)
{
return send(__buffers, __flags,
__throw_on_error{"basic_datagram_socket::send"});
}
template<typename _ConstBufferSequence>
size_t
send(const _ConstBufferSequence& __buffers,
socket_base::message_flags __flags, error_code& __ec)
{
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
socket_base::__msg_hdr __msg(__buffers);
ssize_t __result = ::sendmsg(this->native_handle(), &__msg,
static_cast<int>(__flags));
if (__result == -1)
{
__ec.assign(errno, generic_category());
return 0;
}
__ec.clear();
return __result;
#else
__ec = std::make_error_code(errc::operation_not_supported);
return 0;
#endif
}
template<typename _ConstBufferSequence, typename _CompletionToken>
__deduced_t<_CompletionToken, void(error_code, size_t)>
async_send(const _ConstBufferSequence& __buffers,
_CompletionToken&& __token)
{
return async_send(__buffers, socket_base::message_flags(),
std::forward<_CompletionToken>(__token));
}
template<typename _ConstBufferSequence, typename _CompletionToken>
__deduced_t<_CompletionToken, void(error_code, size_t)>
async_send(const _ConstBufferSequence& __buffers,
socket_base::message_flags __flags,
_CompletionToken&& __token)
{
async_completion<_CompletionToken, void(error_code, size_t)>
__init{__token};
this->get_executor().context().async_wait( this->native_handle(),
socket_base::wait_write,
[__h = std::move(__init.completion_handler),
&__buffers, __flags = static_cast<int>(__flags),
__fd = this->native_handle()]
(error_code __ec) mutable {
if (__ec)
{
__h(__ec);
return;
}
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
socket_base::__msg_hdr __msg(__buffers);
ssize_t __result = ::sendmsg(__fd, &__msg, __flags);
if (__result == -1)
{
__ec.assign(errno, generic_category());
__result = 0;
}
else
__ec.clear();
__h(__ec, __result);
#else
__h(std::make_error_code(errc::operation_not_supported), 0);
#endif
});
return __init.result.get();
}
template<typename _ConstBufferSequence>
size_t
send_to(const _ConstBufferSequence& __buffers,
const endpoint_type& __recipient)
{
return send_to(__buffers, __recipient,
socket_base::message_flags(),
__throw_on_error{"basic_datagram_socket::send_to"});
}
template<typename _ConstBufferSequence>
size_t
send_to(const _ConstBufferSequence& __buffers,
const endpoint_type& __recipient, error_code& __ec)
{
return send_to(__buffers, __recipient,
socket_base::message_flags(), __ec);
}
template<typename _ConstBufferSequence>
size_t
send_to(const _ConstBufferSequence& __buffers,
const endpoint_type& __recipient,
socket_base::message_flags __flags)
{
return send_to(__buffers, __recipient, __flags,
__throw_on_error{"basic_datagram_socket::send_to"});
}
template<typename _ConstBufferSequence>
size_t
send_to(const _ConstBufferSequence& __buffers,
const endpoint_type& __recipient,
socket_base::message_flags __flags, error_code& __ec)
{
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
socket_base::__msg_hdr __msg(__buffers, __recipient);
ssize_t __result = ::sendmsg(this->native_handle(), &__msg,
static_cast<int>(__flags));
if (__result == -1)
{
__ec.assign(errno, generic_category());
return 0;
}
__ec.clear();
__recipient.resize(__msg.msg_namelen);
return __result;
#else
__ec = std::make_error_code(errc::operation_not_supported);
return 0;
#endif
}
template<typename _ConstBufferSequence, typename _CompletionToken>
__deduced_t<_CompletionToken, void(error_code, size_t)>
async_send_to(const _ConstBufferSequence& __buffers,
const endpoint_type& __recipient,
_CompletionToken&& __token)
{
return async_send_to(__buffers, __recipient,
socket_base::message_flags(),
std::forward<_CompletionToken>(__token));
}
template<typename _ConstBufferSequence, typename _CompletionToken>
__deduced_t<_CompletionToken, void(error_code, size_t)>
async_send_to(const _ConstBufferSequence& __buffers,
const endpoint_type& __recipient,
socket_base::message_flags __flags,
_CompletionToken&& __token)
{
async_completion<_CompletionToken, void(error_code, size_t)>
__init{__token};
this->get_executor().context().async_wait( this->native_handle(),
socket_base::wait_write,
[__h = std::move(__init.completion_handler),
&__buffers, __flags = static_cast<int>(__flags),
__recipient = std::move(__recipient),
__fd = this->native_handle()]
(error_code __ec) mutable {
if (__ec)
{
__h(__ec);
return;
}
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
socket_base::__msg_hdr __msg(__buffers, __recipient);
ssize_t __result = ::sendmsg(__fd, &__msg, __flags);
if (__result == -1)
{
__ec.assign(errno, generic_category());
__result = 0;
}
else
{
__ec.clear();
__recipient.resize(__msg.msg_namelen);
}
__h(__ec, __result);
#else
__h(std::make_error_code(errc::operation_not_supported), 0);
#endif
});
return __init.result.get();
}
};
template<typename _Protocol>
class basic_stream_socket : public basic_socket<_Protocol>
{
using __base = basic_socket<_Protocol>;
public:
// types:
typedef int native_handle_type;
typedef _Protocol protocol_type;
typedef typename protocol_type::endpoint endpoint_type;
// construct / copy / destroy:
explicit
basic_stream_socket(io_context& __ctx) : __base(__ctx) { }
basic_stream_socket(io_context& __ctx, const protocol_type& __protocol)
: __base(__ctx, __protocol) { }
basic_stream_socket(io_context& __ctx, const endpoint_type& __endpoint)
: __base(__ctx, __endpoint) { }
basic_stream_socket(io_context& __ctx, const protocol_type& __protocol,
const native_handle_type& __native_socket)
: __base(__ctx, __protocol, __native_socket) { }
basic_stream_socket(const basic_stream_socket&) = delete;
basic_stream_socket(basic_stream_socket&& __rhs) = default;
template<typename _OtherProtocol, typename _Requires
= _Require<is_convertible<_OtherProtocol, _Protocol>>>
basic_stream_socket(basic_stream_socket<_OtherProtocol>&& __rhs)
: __base(std::move(__rhs)) { }
~basic_stream_socket() = default;
basic_stream_socket& operator=(const basic_stream_socket&) = delete;
basic_stream_socket& operator=(basic_stream_socket&& __rhs) = default;
template<class _OtherProtocol>
enable_if_t<is_convertible<_OtherProtocol, _Protocol>::value,
basic_stream_socket&>
operator=(basic_stream_socket<_OtherProtocol>&& __rhs)
{
__base::operator=(std::move(__rhs));
return *this;
}
// basic_stream_socket operations:
template<class _MutableBufferSequence>
size_t
receive(const _MutableBufferSequence& __buffers)
{
return receive(__buffers, socket_base::message_flags(),
__throw_on_error{"basic_stream_socket::receive"});
}
template<class _MutableBufferSequence>
size_t
receive(const _MutableBufferSequence& __buffers, error_code& __ec)
{ return receive(__buffers, socket_base::message_flags(), __ec); }
template<class _MutableBufferSequence>
size_t
receive(const _MutableBufferSequence& __buffers,
socket_base::message_flags __flags)
{
return receive(__buffers, __flags,
__throw_on_error{"basic_stream_socket::receive"});
}
template<class _MutableBufferSequence>
size_t
receive(const _MutableBufferSequence& __buffers,
socket_base::message_flags __flags, error_code& __ec)
{
if (__buffer_empty(__buffers))
{
__ec.clear();
return 0;
}
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
socket_base::__msg_hdr __msg(__buffers);
ssize_t __result = ::recvmsg(this->native_handle(), &__msg,
static_cast<int>(__flags));
if (__result >= 0)
{
__ec.clear();
return __result;
}
__ec.assign(errno, generic_category());
#else
__ec = std::make_error_code(errc::operation_not_supported);
#endif
return 0;
}
template<class _MutableBufferSequence, class _CompletionToken>
__deduced_t<_CompletionToken, void(error_code, size_t)>
async_receive(const _MutableBufferSequence& __buffers,
_CompletionToken&& __token)
{
return async_receive(__buffers, socket_base::message_flags(),
std::forward<_CompletionToken>(__token));
}
template<class _MutableBufferSequence, class _CompletionToken>
__deduced_t<_CompletionToken, void(error_code, size_t)>
async_receive(const _MutableBufferSequence& __buffers,
socket_base::message_flags __flags,
_CompletionToken&& __token)
{
async_completion<_CompletionToken, void(error_code, size_t)>
__init{__token};
if (__buffer_empty(__buffers))
{
auto __ex = net::get_associated_executor(
__init.completion_handler, this->get_executor());
auto __a = get_associated_allocator(
__init.completion_handler, std::allocator<void>());
__ex.post(
[__h=std::move(__init.completion_handler)] () mutable
{ __h(error_code{}, 0); }, __a);
return __init.result.get();
}
this->get_executor().context().async_wait(this->native_handle(),
socket_base::wait_read,
[__h = std::move(__init.completion_handler),
&__buffers, __flags = static_cast<int>(__flags),
__fd = this->native_handle()]
(error_code __ec) mutable {
if (__ec)
{
__h(__ec);
return;
}
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
socket_base::__msg_hdr __msg(__buffers);
ssize_t __result = ::recvmsg(__fd, &__msg, __flags);
if (__result == -1)
{
__ec.assign(errno, generic_category());
__result = 0;
}
else
__ec.clear();
__h(__ec, __result);
#else
__h(std::make_error_code(errc::operation_not_supported), 0);
#endif
});
return __init.result.get();
}
template<class _ConstBufferSequence>
size_t
send(const _ConstBufferSequence& __buffers)
{
return send(__buffers, socket_base::message_flags(),
__throw_on_error{"basic_stream_socket::send"});
}
template<class _ConstBufferSequence>
size_t
send(const _ConstBufferSequence& __buffers, error_code& __ec)
{ return send(__buffers, socket_base::message_flags(), __ec); }
template<class _ConstBufferSequence>
size_t
send(const _ConstBufferSequence& __buffers,
socket_base::message_flags __flags)
{
return send(__buffers, socket_base::message_flags(),
__throw_on_error{"basic_stream_socket::send"});
}
template<class _ConstBufferSequence>
size_t
send(const _ConstBufferSequence& __buffers,
socket_base::message_flags __flags, error_code& __ec)
{
if (__buffer_empty(__buffers))
{
__ec.clear();
return 0;
}
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
socket_base::__msg_hdr __msg(__buffers);
ssize_t __result = ::sendmsg(this->native_handle(), &__msg,
static_cast<int>(__flags));
if (__result >= 0)
{
__ec.clear();
return __result;
}
__ec.assign(errno, generic_category());
#else
__ec = std::make_error_code(errc::operation_not_supported);
#endif
return 0;
}
template<class _ConstBufferSequence, class _CompletionToken>
__deduced_t<_CompletionToken, void(error_code, size_t)>
async_send(const _ConstBufferSequence& __buffers,
_CompletionToken&& __token)
{
return async_send(__buffers, socket_base::message_flags(),
std::forward<_CompletionToken>(__token));
}
template<class _ConstBufferSequence, class _CompletionToken>
__deduced_t<_CompletionToken, void(error_code, size_t)>
async_send(const _ConstBufferSequence& __buffers,
socket_base::message_flags __flags,
_CompletionToken&& __token)
{
async_completion<_CompletionToken, void(error_code, size_t)>
__init{__token};
if (__buffer_empty(__buffers))
{
auto __ex = net::get_associated_executor(
__init.completion_handler, this->get_executor());
auto __a = get_associated_allocator(
__init.completion_handler, std::allocator<void>());
__ex.post(
[__h=std::move(__init.completion_handler)] () mutable
{ __h(error_code{}, 0); }, __a);
return __init.result.get();
}
this->get_executor().context().async_wait(this->native_handle(),
socket_base::wait_write,
[__h = std::move(__init.completion_handler),
&__buffers, __flags = static_cast<int>(__flags),
__fd = this->native_handle()]
(error_code __ec) mutable {
if (__ec)
{
__h(__ec);
return;
}
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
socket_base::__msg_hdr __msg(__buffers);
ssize_t __result = ::sendmsg(__fd, &__msg, __flags);
if (__result == -1)
{
__ec.assign(errno, generic_category());
__result = 0;
}
else
__ec.clear();
__h(__ec, __result);
#else
__h(std::make_error_code(errc::operation_not_supported), 0);
#endif
});
return __init.result.get();
}
template<class _MutableBufferSequence>
size_t
read_some(const _MutableBufferSequence& __buffers)
{
return receive(__buffers,
__throw_on_error{"basic_stream_socket::read_some"});
}
template<class _MutableBufferSequence>
size_t
read_some(const _MutableBufferSequence& __buffers, error_code& __ec)
{ return receive(__buffers, __ec); }
template<class _MutableBufferSequence, class _CompletionToken>
__deduced_t<_CompletionToken, void(error_code, size_t)>
async_read_some(const _MutableBufferSequence& __buffers,
_CompletionToken&& __token)
{
return async_receive(__buffers,
std::forward<_CompletionToken>(__token));
}
template<class _ConstBufferSequence>
size_t
write_some(const _ConstBufferSequence& __buffers)
{
return send(__buffers,
__throw_on_error{"basic_stream_socket:write_some"});
}
template<class _ConstBufferSequence>
size_t
write_some(const _ConstBufferSequence& __buffers, error_code& __ec)
{ return send(__buffers, __ec); }
template<class _ConstBufferSequence, class _CompletionToken>
__deduced_t<_CompletionToken, void(error_code, size_t)>
async_write_some(const _ConstBufferSequence& __buffers,
_CompletionToken&& __token)
{
return async_send(__buffers,
std::forward<_CompletionToken>(__token));
}
};
template<typename _AcceptableProtocol>
class basic_socket_acceptor
: public socket_base, private __basic_socket_impl<_AcceptableProtocol>
{
using __base = __basic_socket_impl<_AcceptableProtocol>;
public:
// types:
typedef io_context::executor_type executor_type;
typedef int native_handle_type;
typedef _AcceptableProtocol protocol_type;
typedef typename protocol_type::endpoint endpoint_type;
typedef typename protocol_type::socket socket_type;
// construct / copy / destroy:
explicit
basic_socket_acceptor(io_context& __ctx)
: __base(__ctx), _M_protocol(endpoint_type{}.protocol()) { }
basic_socket_acceptor(io_context& __ctx,
const protocol_type& __protocol)
: __base(__ctx), _M_protocol(__protocol)
{ open(__protocol); }
basic_socket_acceptor(io_context& __ctx, const endpoint_type& __endpoint,
bool __reuse_addr = true)
: basic_socket_acceptor(__ctx, __endpoint.protocol())
{
if (__reuse_addr)
set_option(reuse_address(true));
bind(__endpoint);
listen();
}
basic_socket_acceptor(io_context& __ctx, const protocol_type& __protocol,
const native_handle_type& __native_acceptor)
: basic_socket_acceptor(__ctx, __protocol)
{ assign(__protocol, __native_acceptor); }
basic_socket_acceptor(const basic_socket_acceptor&) = delete;
basic_socket_acceptor(basic_socket_acceptor&&) = default;
template<typename _OtherProtocol, typename _Requires
= _Require<is_convertible<_OtherProtocol, protocol_type>>>
basic_socket_acceptor(basic_socket_acceptor<_OtherProtocol>&& __rhs)
: __base(std::move(__rhs)) { }
~basic_socket_acceptor() = default;
basic_socket_acceptor& operator=(const basic_socket_acceptor&) = delete;
basic_socket_acceptor& operator=(basic_socket_acceptor&&) = default;
template<class _OtherProtocol>
enable_if_t<is_convertible<_OtherProtocol, protocol_type>::value,
basic_socket_acceptor&>
operator=(basic_socket_acceptor<_OtherProtocol>&& __rhs)
{
__base::operator=(std::move(__rhs));
return *this;
}
// basic_socket_acceptor operations:
executor_type get_executor() noexcept { return __base::get_executor(); }
native_handle_type
native_handle() noexcept { return __base::native_handle(); }
void
open(const protocol_type& __protocol = protocol_type())
{ open(__protocol, __throw_on_error{"basic_socket_acceptor::open"}); }
void
open(const protocol_type& __protocol, error_code& __ec)
{ __base::open(__protocol, __ec); }
void
assign(const protocol_type& __protocol,
const native_handle_type& __native_acceptor)
{
assign(__protocol, __native_acceptor,
__throw_on_error{"basic_socket_acceptor::assign"});
}
void
assign(const protocol_type& __protocol,
const native_handle_type& __native_acceptor,
error_code& __ec)
{ __base::assign(__protocol, __native_acceptor, __ec); }
bool
is_open() const noexcept { return __base::is_open(); }
void
close() { close(__throw_on_error{"basic_socket_acceptor::close"}); }
void
close(error_code& __ec) { __base::_close(__ec); }
void
cancel() { cancel(__throw_on_error{"basic_socket_acceptor::cancel"}); }
void
cancel(error_code& __ec) { __base::cancel(__ec); }
template<typename _SettableSocketOption>
void
set_option(const _SettableSocketOption& __option)
{
set_option(__option,
__throw_on_error{"basic_socket_acceptor::set_option"});
}
template<typename _SettableSocketOption>
void
set_option(const _SettableSocketOption& __option, error_code& __ec)
{ __base::set_option(__option, __ec); }
template<typename _GettableSocketOption>
void
get_option(_GettableSocketOption& __option) const
{
get_option(__option,
__throw_on_error{"basic_socket_acceptor::get_option"});
}
template<typename _GettableSocketOption>
void
get_option(_GettableSocketOption& __option, error_code& __ec) const
{ __base::get_option(__option, __ec); }
template<typename _IoControlCommand>
void
io_control(_IoControlCommand& __command)
{
io_control(__command,
__throw_on_error{"basic_socket_acceptor::io_control"});
}
template<typename _IoControlCommand>
void
io_control(_IoControlCommand& __command, error_code& __ec)
{ __base::io_control(__command, __ec); }
void
non_blocking(bool __mode)
{
non_blocking(__mode,
__throw_on_error{"basic_socket_acceptor::non_blocking"});
}
void
non_blocking(bool __mode, error_code& __ec)
{ __base::non_blocking(__mode, __ec); }
bool non_blocking() const { return __base::non_blocking(); }
void
native_non_blocking(bool __mode)
{
native_non_blocking(__mode, __throw_on_error{
"basic_socket_acceptor::native_non_blocking"});
}
void
native_non_blocking(bool __mode, error_code& __ec)
{ __base::native_non_blocking(__mode, __ec); }
bool
native_non_blocking() const
{ return __base::native_non_blocking(); }
void
bind(const endpoint_type& __endpoint)
{
return bind(__endpoint,
__throw_on_error{"basic_socket_acceptor::bind"});
}
void
bind(const endpoint_type& __endpoint, error_code& __ec)
{ __base::bind(__endpoint, __ec); }
void
listen(int __backlog = max_listen_connections)
{
return listen(__backlog,
__throw_on_error{"basic_socket_acceptor::listen"});
}
void
listen(int __backlog, error_code& __ec)
{
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
if (::listen(native_handle(), __backlog) == -1)
__ec.assign(errno, generic_category());
else
__ec.clear();
#else
__ec = std::make_error_code(errc::operation_not_supported);
#endif
}
endpoint_type
local_endpoint() const
{
return local_endpoint(
__throw_on_error{"basic_socket_acceptor::local_endpoint"});
}
endpoint_type
local_endpoint(error_code& __ec) const
{ return __base::local_endpoint(__ec); }
void
enable_connection_aborted(bool __mode)
{ __base::_M_bits.enable_connection_aborted = __mode; }
bool
enable_connection_aborted() const
{ return __base::_M_bits.enable_connection_aborted; }
socket_type
accept()
{ return accept(__throw_on_error{"basic_socket_acceptor::accept"}); }
socket_type
accept(error_code& __ec)
{ return accept(get_executor().context(), __ec); }
socket_type accept(io_context& __ctx)
{
return accept(__ctx,
__throw_on_error{"basic_socket_acceptor::accept"});
}
socket_type
accept(io_context& __ctx, error_code& __ec)
{
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
do
{
int __h = ::accept(native_handle(), nullptr, 0);
if (__h != -1)
{
__ec.clear();
return socket_type{__ctx, _M_protocol, __h};
}
} while (errno == ECONNABORTED && enable_connection_aborted());
__ec.assign(errno, generic_category());
#else
__ec = std::make_error_code(errc::operation_not_supported);
#endif
return socket_type{__ctx};
}
template<class _CompletionToken>
__deduced_t<_CompletionToken, void(error_code, socket_type)>
async_accept(_CompletionToken&& __token)
{
return async_accept(get_executor().context(),
std::forward<_CompletionToken>(__token));
}
template<class _CompletionToken>
__deduced_t<_CompletionToken, void(error_code, socket_type)>
async_accept(io_context& __ctx, _CompletionToken&& __token)
{
async_completion<_CompletionToken, void(error_code, socket_type)>
__init{__token};
__ctx.get_executor().context().async_wait(native_handle(),
socket_base::wait_read,
[__h = std::move(__init.completion_handler),
__connabort = enable_connection_aborted(),
__fd = native_handle(),
__protocol = _M_protocol,
&__ctx
]
(error_code __ec) mutable {
if (__ec)
{
__h(__ec, socket_type(__ctx));
return;
}
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
do
{
int __newfd = ::accept(__fd, nullptr, 0);
if (__newfd != -1)
{
__ec.clear();
__h(__ec, socket_type{__ctx, __protocol, __newfd});
return;
}
} while (errno == ECONNABORTED && __connabort);
__ec.assign(errno, generic_category());
__h(__ec, socket_type(__ctx));
#else
__h(std::make_error_code(errc::operation_not_supported), 0);
#endif
});
return __init.result.get();
}
socket_type
accept(endpoint_type& __endpoint)
{
return accept(get_executor().context(), __endpoint,
__throw_on_error{"basic_socket_acceptor::accept"});
}
socket_type
accept(endpoint_type& __endpoint, error_code& __ec)
{ return accept(get_executor().context(), __endpoint, __ec); }
socket_type
accept(io_context& __ctx, endpoint_type& __endpoint)
{
return accept(__ctx, __endpoint,
__throw_on_error{"basic_socket_acceptor::accept"});
}
socket_type
accept(io_context& __ctx, endpoint_type& __endpoint, error_code& __ec)
{
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
do
{
socklen_t __len = __endpoint.capacity();
int __h = ::accept(native_handle(), (sockaddr*)__endpoint.data(),
&__len);
if (__h != -1)
{
__endpoint.resize(__len);
return socket_type{__ctx, _M_protocol, __h};
}
} while (errno == ECONNABORTED && enable_connection_aborted());
__ec.assign(errno, generic_category());
#else
__ec = std::make_error_code(errc::operation_not_supported);
#endif
return socket_type{__ctx};
}
template<class _CompletionToken>
__deduced_t<_CompletionToken, void(error_code, socket_type)>
async_accept(endpoint_type& __endpoint,
_CompletionToken&& __token)
{
return async_accept(get_executor().context(), __endpoint,
std::forward<_CompletionToken>(__token));
}
template<class _CompletionToken>
__deduced_t<_CompletionToken, void(error_code, socket_type)>
async_accept(io_context& __ctx, endpoint_type& __endpoint,
_CompletionToken&& __token)
{
async_completion<_CompletionToken, void(error_code, socket_type)>
__init{__token};
__ctx.get_executor().context().async_wait(native_handle(),
socket_base::wait_read,
[__h = std::move(__init.completion_handler),
__ep = std::move(__endpoint),
__connabort = enable_connection_aborted(),
__fd = native_handle(),
&__ctx
]
(error_code __ec) mutable {
if (__ec)
{
__h(__ec, socket_type(__ctx));
return;
}
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
do
{
socklen_t __len = __ep.capacity();
int __newfd = ::accept(__fd, __ep.data, &__len);
if (__newfd != -1)
{
__ep.resize(__len);
auto __protocol = __ep.protocol();
__ec.clear();
__h(__ec, socket_type{__ctx, __protocol, __newfd});
return;
}
} while (errno == ECONNABORTED && __connabort);
__ec.assign(errno, generic_category());
#else
__ec = std::make_error_code(errc::operation_not_supported);
#endif
__h(__ec, socket_type(__ctx));
});
return __init.result.get();
}
void
wait(wait_type __w)
{ wait(__w, __throw_on_error{"basic_socket_acceptor::wait"}); }
void
wait(wait_type __w, error_code& __ec)
{
#ifdef _GLIBCXX_HAVE_POLL_H
::pollfd __fds;
__fds.fd = native_handle();
__fds.events = __w; // __w | POLLIN;
if (::poll(&__fds, 1, -1) == -1)
__ec.assign(errno, generic_category());
else
__ec.clear();
#else
__ec = std::make_error_code(errc::operation_not_supported);
#endif
}
template<class _CompletionToken>
__deduced_t<_CompletionToken, void(error_code)>
async_wait(wait_type __w, _CompletionToken&& __token)
{
async_completion<_CompletionToken, void(error_code)> __init{__token};
get_executor().context().async_wait( native_handle(),
static_cast<int>(__w),
[__h = std::move(__init.completion_handler)]
(error_code __ec) mutable {
__h(__ec);
});
return __init.result.get();
}
private:
protocol_type _M_protocol;
};
// @}
/** @brief Socket streams
* @{
*/
template<typename _Protocol, typename _Clock, typename _WaitTraits>
class basic_socket_streambuf : public basic_streambuf<char>
{
public:
// types:
typedef _Protocol protocol_type;
typedef typename protocol_type::endpoint endpoint_type;
typedef _Clock clock_type;
typedef typename clock_type::time_point time_point;
typedef typename clock_type::duration duration;
typedef _WaitTraits wait_traits_type;
// construct / copy / destroy:
basic_socket_streambuf() : _M_socket(_S_ctx()) { }
explicit
basic_socket_streambuf(basic_stream_socket<protocol_type> __s)
: _M_socket(std::move(__s)) { }
basic_socket_streambuf(const basic_socket_streambuf&) = delete;
basic_socket_streambuf(basic_socket_streambuf&& __rhs); // TODO
virtual ~basic_socket_streambuf(); // TODO
basic_socket_streambuf& operator=(const basic_socket_streambuf&) = delete;
basic_socket_streambuf& operator=(basic_socket_streambuf&& __rhs); // TODO
// members:
basic_socket_streambuf* connect(const endpoint_type& __e); // TODO
template<typename... _Args>
basic_socket_streambuf* connect(_Args&&... ); // TODO
basic_socket_streambuf* close(); // TODO
basic_socket<protocol_type>& socket() { return _M_socket; }
error_code error() const { return _M_ec; }
time_point expiry() const { return _M_expiry; }
void
expires_at(const time_point& __t)
{ _M_expiry = __t; }
void
expires_after(const duration& __d)
{ expires_at(clock_type::now() + __d); }
protected:
// overridden virtual functions: // TODO
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:
static io_context&
_S_ctx()
{
static io_context __ctx;
return __ctx;
}
basic_stream_socket<protocol_type> _M_socket;
error_code _M_ec;
time_point _M_expiry{ time_point::max() };
};
template<typename _Protocol, class _Clock, typename _WaitTraits>
class basic_socket_iostream : public basic_iostream<char>
{
using __streambuf_type
= basic_socket_streambuf<_Protocol, _Clock, _WaitTraits>;
public:
// types:
typedef _Protocol protocol_type;
typedef typename protocol_type::endpoint endpoint_type;
typedef _Clock clock_type;
typedef typename clock_type::time_point time_point;
typedef typename clock_type::duration duration;
typedef _WaitTraits wait_traits_type;
// construct / copy / destroy:
// TODO base-from-member ?
basic_socket_iostream() : basic_iostream(nullptr), _M_sb()
{
this->init(std::addressof(_M_sb));
this->setf(std::ios::unitbuf);
}
explicit
basic_socket_iostream(basic_stream_socket<protocol_type> __s)
: basic_iostream(nullptr), _M_sb(std::move(__s))
{
this->init(std::addressof(_M_sb));
this->setf(std::ios::unitbuf);
}
basic_socket_iostream(const basic_socket_iostream&) = delete;
basic_socket_iostream(basic_socket_iostream&& __rhs)
: basic_iostream(nullptr), _M_sb(std::move(__rhs._M_sb))
// XXX ??? ^^^^^^^
{
// XXX ??? this->init(std::addressof(_M_sb));
this->set_rbduf(std::addressof(_M_sb));
}
template<typename... _Args>
explicit
basic_socket_iostream(_Args&&... __args)
: basic_iostream(nullptr), _M_sb()
{
this->init(std::addressof(_M_sb));
this->setf(std::ios::unitbuf);
connect(forward<_Args>(__args)...);
}
basic_socket_iostream& operator=(const basic_socket_iostream&) = delete;
basic_socket_iostream& operator=(basic_socket_iostream&& __rhs); // TODO
// members:
template<typename... _Args>
void
connect(_Args&&... __args)
{
if (rdbuf()->connect(forward<_Args>(__args)...) == nullptr)
this->setstate(failbit);
}
void
close()
{
if (rdbuf()->close() == nullptr)
this->setstate(failbit);
}
basic_socket_streambuf<protocol_type, clock_type, wait_traits_type>*
rdbuf() const
{ return const_cast<__streambuf_type*>(std::addressof(_M_sb)); }
basic_socket<protocol_type>& socket() { return rdbuf()->socket(); }
error_code error() const { return rdbuf()->error(); }
time_point expiry() const { return rdbuf()->expiry(); }
void expires_at(const time_point& __t) { rdbuf()->expires_at(__t); }
void expires_after(const duration& __d) { rdbuf()->expires_after(__d); }
private:
__streambuf_type _M_sb;
};
// @}
/** @brief synchronous connect operations
* @{
*/
template<typename _Protocol, typename _EndpointSequence,
typename _ConnectCondition>
inline typename _Protocol::endpoint
connect(basic_socket<_Protocol>& __s,
const _EndpointSequence& __endpoints,
_ConnectCondition __c, error_code& __ec)
{
__ec.clear();
bool __found = false;
for (auto& __ep : __endpoints)
{
if (__c(__ec, __ep))
{
__found = true;
__s.close(__ec);
if (!__ec)
__s.open(__ep.protocol(), __ec);
if (!__ec)
__s.connect(__ep, __ec);
if (!__ec)
return __ep;
}
}
if (!__found)
__ec = socket_errc::not_found;
return typename _Protocol::endpoint{};
}
template<typename _Protocol, typename _InputIterator,
typename _ConnectCondition>
inline _InputIterator
connect(basic_socket<_Protocol>& __s,
_InputIterator __first, _InputIterator __last,
_ConnectCondition __c, error_code& __ec)
{
__ec.clear();
bool __found = false;
for (auto __i = __first; __i != __last; ++__i)
{
if (__c(__ec, *__i))
{
__found = true;
__s.close(__ec);
if (!__ec)
__s.open(typename _Protocol::endpoint(*__i).protocol(), __ec);
if (!__ec)
__s.connect(*__i, __ec);
if (!__ec)
return __i;
}
}
if (!__found)
__ec = socket_errc::not_found;
return __last;
}
template<typename _Protocol, typename _EndpointSequence,
typename _ConnectCondition>
inline typename _Protocol::endpoint
connect(basic_socket<_Protocol>& __s,
const _EndpointSequence& __endpoints,
_ConnectCondition __c)
{
return net::connect(__s, __endpoints, __c, __throw_on_error{"connect"});
}
template<typename _Protocol, typename _InputIterator,
typename _ConnectCondition>
inline _InputIterator
connect(basic_socket<_Protocol>& __s,
_InputIterator __first, _InputIterator __last,
_ConnectCondition __c)
{
return net::connect(__s, __first, __last, __c,
__throw_on_error{"connect"});
}
template<typename _Protocol, typename _EndpointSequence>
inline typename _Protocol::endpoint
connect(basic_socket<_Protocol>& __s,
const _EndpointSequence& __endpoints)
{
return net::connect(__s, __endpoints, [](auto, auto){ return true; },
__throw_on_error{"connect"});
}
template<typename _Protocol, typename _EndpointSequence>
inline typename _Protocol::endpoint
connect(basic_socket<_Protocol>& __s,
const _EndpointSequence& __endpoints,
error_code& __ec)
{
return net::connect(__s, __endpoints, [](auto, auto){ return true; },
__ec);
}
template<typename _Protocol, typename _InputIterator>
inline _InputIterator
connect(basic_socket<_Protocol>& __s,
_InputIterator __first, _InputIterator __last)
{
return net::connect(__s, __first, __last, [](auto, auto){ return true; },
__throw_on_error{"connect"});
}
template<typename _Protocol, typename _InputIterator>
inline _InputIterator
connect(basic_socket<_Protocol>& __s,
_InputIterator __first, _InputIterator __last,
error_code& __ec)
{
return net::connect(__s, __first, __last, [](auto, auto){ return true; },
__ec);
}
// @}
/** @brief asynchronous connect operations
* @{
*/
template<typename _Protocol, typename _EndpointSequence,
typename _ConnectCondition, typename _CompletionToken>
inline
__deduced_t<_CompletionToken,
void(error_code, typename _Protocol::endpoint)>
async_connect(basic_socket<_Protocol>& __s,
const _EndpointSequence& __endpoints,
_ConnectCondition __c, _CompletionToken&& __token); // TODO
template<typename _Protocol, typename _EndpointSequence,
typename _CompletionToken>
inline
__deduced_t<_CompletionToken,
void(error_code, typename _Protocol::endpoint)>
async_connect(basic_socket<_Protocol>& __s,
const _EndpointSequence& __endpoints,
_CompletionToken&& __token)
{
return net::async_connect(__s, __endpoints,
[](auto, auto){ return true; },
forward<_CompletionToken>(__token));
}
template<typename _Protocol, typename _InputIterator,
typename _ConnectCondition, typename _CompletionToken>
inline
__deduced_t<_CompletionToken, void(error_code, _InputIterator)>
async_connect(basic_socket<_Protocol>& __s,
_InputIterator __first, _InputIterator __last,
_ConnectCondition __c, _CompletionToken&& __token); // TODO
template<typename _Protocol, typename _InputIterator,
typename _CompletionToken>
inline
__deduced_t<_CompletionToken, void(error_code, _InputIterator)>
async_connect(basic_socket<_Protocol>& __s,
_InputIterator __first, _InputIterator __last,
_CompletionToken&& __token)
{
return net::async_connect(__s, __first, __last,
[](auto, auto){ return true; },
forward<_CompletionToken>(__token));
}
// @}
#endif // _GLIBCXX_HAVE_UNISTD_H
// @}
} // namespace v1
} // namespace net
} // namespace experimental
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std
#endif // C++14
#endif // _GLIBCXX_EXPERIMENTAL_SOCKET