From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2181) id 196C53858423; Tue, 24 Aug 2021 16:00:37 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 196C53858423 MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Jonathan Wakely To: gcc-cvs@gcc.gnu.org, libstdc++-cvs@gcc.gnu.org Subject: [gcc r11-8911] libstdc++: Fix internet socket option classes X-Act-Checkin: gcc X-Git-Author: Jonathan Wakely X-Git-Refname: refs/heads/releases/gcc-11 X-Git-Oldrev: 91e84187e4ddac74135f1540065c25816cad044c X-Git-Newrev: fc5325158fbcd1d4e9bb80bfd32a4a2a365a9ed0 Message-Id: <20210824160037.196C53858423@sourceware.org> Date: Tue, 24 Aug 2021 16:00:37 +0000 (GMT) X-BeenThere: libstdc++-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libstdc++-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 24 Aug 2021 16:00:37 -0000 https://gcc.gnu.org/g:fc5325158fbcd1d4e9bb80bfd32a4a2a365a9ed0 commit r11-8911-gfc5325158fbcd1d4e9bb80bfd32a4a2a365a9ed0 Author: Jonathan Wakely Date: Mon Apr 26 21:16:21 2021 +0100 libstdc++: Fix internet socket option classes Similar to the previous commit, this fixes various problems with the socket options classes in the header: - The constructors were not noexcept. - The __sockopt_base::value() member function was present unconditionally (so was defined for socket_base::linger which is incorrect). - The __socket_crtp::operator=(T) assignment operator was not noexcept, and was hidden in the derived classes. - The MulticastSocketOptions incorrectly used a union, incorrectly defined resize and const data() member functions, and their constructors were unimplemented. Also, where appropriate: - Use class instead of struct for the socket option types. - Define the _S_level and _S_name constants as private. - Declare the __socket_crtp base as a friend. libstdc++-v3/ChangeLog: * include/experimental/internet (tcp::no_delay, v6_only) (unicast::hops, multicast::hops, multicast::enable_loopback): Change access of base class and static data members. Add using-declaration for __socket_crtp::operator=(_Tp). (multicast::__mcastopt): New type. (multicast::join_group, multicast::leave_group): Derive from __mcastopt for common implementation. * include/experimental/socket: Add comment. * testsuite/experimental/net/internet/socket/opt.cc: New test. * testsuite/experimental/net/socket/socket_base.cc: Check for protected constructor/destructor of socket_base. Check for explicit constructors of socket option classes. (cherry picked from commit 2e0b1c6ce3afe0670b96444c6b955ce184ed0046) Diff: --- libstdc++-v3/include/experimental/internet | 185 ++++++++++----------- libstdc++-v3/include/experimental/socket | 2 + .../experimental/net/internet/socket/opt.cc | 151 +++++++++++++++++ .../experimental/net/socket/socket_base.cc | 15 +- 4 files changed, 251 insertions(+), 102 deletions(-) diff --git a/libstdc++-v3/include/experimental/internet b/libstdc++-v3/include/experimental/internet index d3321afb9c6..2e3fd06ead2 100644 --- a/libstdc++-v3/include/experimental/internet +++ b/libstdc++-v3/include/experimental/internet @@ -52,7 +52,7 @@ # include // inet_ntop #endif #ifdef _GLIBCXX_HAVE_NETINET_IN_H -# include // IPPROTO_IP +# include // IPPROTO_IP, IPPROTO_IPV6, in_addr, in6_addr #endif #ifdef _GLIBCXX_HAVE_NETINET_TCP_H # include // TCP_NODELAY @@ -2078,6 +2078,7 @@ namespace ip struct no_delay : __sockopt_crtp { using __sockopt_crtp::__sockopt_crtp; + using __sockopt_crtp::operator=; static const int _S_level = IPPROTO_TCP; static const int _S_name = TCP_NODELAY; @@ -2157,10 +2158,14 @@ namespace ip /// @} /// Restrict a socket created for an IPv6 protocol to IPv6 only. - struct v6_only : __sockopt_crtp + class v6_only : public __sockopt_crtp { + public: using __sockopt_crtp::__sockopt_crtp; + using __sockopt_crtp::operator=; + private: + friend __sockopt_crtp; static const int _S_level = IPPROTO_IPV6; static const int _S_name = IPV6_V6ONLY; }; @@ -2168,9 +2173,11 @@ namespace ip namespace unicast { /// Set the default number of hops (TTL) for outbound datagrams. - struct hops : __sockopt_crtp + class hops : public __sockopt_crtp { + public: using __sockopt_crtp::__sockopt_crtp; + using __sockopt_crtp::operator=; template int @@ -2186,130 +2193,111 @@ namespace ip namespace multicast { - /// Request that a socket joins a multicast group. - struct join_group + class __mcastopt { + public: explicit - join_group(const address&); + __mcastopt(const address& __grp) noexcept + : __mcastopt(__grp.is_v4() ? __mcastopt(__grp.to_v4()) : __mcastopt(__grp.to_v6())) + { } explicit - join_group(const address_v4&, const address_v4& = address_v4::any()); + __mcastopt(const address_v4& __grp, + const address_v4& __iface = address_v4::any()) noexcept + { +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + _M_v4.imr_multiaddr.s_addr = __grp.to_uint(); + _M_v4.imr_interface.s_addr = __iface.to_uint(); +#else + _M_v4.imr_multiaddr.s_addr = __builtin_bswap32(__grp.to_uint()); + _M_v4.imr_interface.s_addr = __builtin_bswap32(__iface.to_uint()); +#endif + } explicit - join_group(const address_v6&, unsigned int = 0); + __mcastopt(const address_v6& __grp, unsigned int __iface = 0) noexcept + { + const auto __addr = __grp.to_bytes(); + __builtin_memcpy(_M_v6.ipv6mr_multiaddr.s6_addr, __addr.data(), 16); + _M_v6.ipv6mr_interface = __iface; + } template int level(const _Protocol& __p) const noexcept { return __p.family() == AF_INET6 ? IPPROTO_IPV6 : IPPROTO_IP; } - template - int - name(const _Protocol& __p) const noexcept - { - return __p.family() == AF_INET6 - ? IPV6_JOIN_GROUP : IP_ADD_MEMBERSHIP; - } - template - void* - data(const _Protocol&) noexcept - { return std::addressof(_M_value); } - template const void* - data(const _Protocol&) const noexcept - { return std::addressof(_M_value); } + data(const _Protocol& __p) const noexcept + { return __p.family() == AF_INET6 ? &_M_v6 : &_M_v4; } template size_t size(const _Protocol& __p) const noexcept - { - return __p.family() == AF_INET6 - ? sizeof(_M_value._M_v6) : sizeof(_M_value._M_v4); - } - - template - void - resize(const _Protocol& __p, size_t __s) - { - if (__s != size(__p)) - __throw_length_error("invalid value for socket option resize"); - } + { return __p.family() == AF_INET6 ? sizeof(_M_v6) : sizeof(_M_v4); } - protected: - union - { - ipv6_mreq _M_v6; - ip_mreq _M_v4; - } _M_value; + private: + ipv6_mreq _M_v6 = {}; + ip_mreq _M_v4 = {}; }; - /// Request that a socket leaves a multicast group. - struct leave_group + /// Request that a socket joins a multicast group. + class join_group : private __mcastopt { - explicit - leave_group(const address&); - - explicit - leave_group(const address_v4&, const address_v4& = address_v4::any()); - - explicit - leave_group(const address_v6&, unsigned int = 0); - - template - int - level(const _Protocol& __p) const noexcept - { return __p.family() == AF_INET6 ? IPPROTO_IPV6 : IPPROTO_IP; } + public: + using __mcastopt::__mcastopt; + using __mcastopt::level; + using __mcastopt::data; + using __mcastopt::size; template int name(const _Protocol& __p) const noexcept { - return __p.family() == AF_INET6 - ? IPV6_LEAVE_GROUP : IP_DROP_MEMBERSHIP; + if (__p.family() == AF_INET6) + return IPV6_JOIN_GROUP; + return IP_ADD_MEMBERSHIP; } - template - void* - data(const _Protocol&) noexcept - { return std::addressof(_M_value); } - - template - const void* - data(const _Protocol&) const noexcept - { return std::addressof(_M_value); } + }; - template - size_t - size(const _Protocol& __p) const noexcept - { - return __p.family() == AF_INET6 - ? sizeof(_M_value._M_v6) : sizeof(_M_value._M_v4); - } + /// Request that a socket leaves a multicast group. + class leave_group : private __mcastopt + { + public: + using __mcastopt::__mcastopt; + using __mcastopt::level; + using __mcastopt::data; + using __mcastopt::size; template - void - resize(const _Protocol& __p, size_t __s) + int + name(const _Protocol& __p) const noexcept { - if (__s != size(__p)) - __throw_length_error("invalid value for socket option resize"); + if (__p.family() == AF_INET6) + return IPV6_LEAVE_GROUP; + return IP_DROP_MEMBERSHIP; } - - protected: - union - { - ipv6_mreq _M_v6; - ip_mreq _M_v4; - } _M_value; }; /// Specify the network interface for outgoing multicast datagrams. class outbound_interface { + public: explicit - outbound_interface(const address_v4&); + outbound_interface(const address_v4& __v4) noexcept + { +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + _M_v4.s_addr = __v4.to_uint(); +#else + _M_v4.s_addr = __builtin_bswap32(__v4.to_uint()); +#endif + } explicit - outbound_interface(unsigned int); + outbound_interface(unsigned int __v6) noexcept + : _M_v4(), _M_v6(__v6) + { } template int @@ -2326,28 +2314,25 @@ namespace ip template const void* - data(const _Protocol&) const noexcept - { return std::addressof(_M_value); } + data(const _Protocol& __p) const noexcept + { return __p.family() == AF_INET6 ? &_M_v6 : &_M_v4; } template size_t size(const _Protocol& __p) const noexcept - { - return __p.family() == AF_INET6 - ? sizeof(_M_value._M_v6) : sizeof(_M_value._M_v4); - } + { return __p.family() == AF_INET6 ? sizeof(_M_v6) : sizeof(_M_v4); } - protected: - union { - unsigned _M_v6; - in_addr _M_v4; - } _M_value; + private: + in_addr _M_v4; + unsigned _M_v6 = 0; }; /// Set the default number of hops (TTL) for outbound datagrams. - struct hops : __sockopt_crtp + class hops : public __sockopt_crtp { + public: using __sockopt_crtp::__sockopt_crtp; + using __sockopt_crtp::operator=; template int @@ -2364,9 +2349,11 @@ namespace ip }; /// Set whether datagrams are delivered back to the local application. - struct enable_loopback : __sockopt_crtp + class enable_loopback : public __sockopt_crtp { + public: using __sockopt_crtp::__sockopt_crtp; + using __sockopt_crtp::operator=; template int diff --git a/libstdc++-v3/include/experimental/socket b/libstdc++-v3/include/experimental/socket index 538dc78e72c..18849f607f8 100644 --- a/libstdc++-v3/include/experimental/socket +++ b/libstdc++-v3/include/experimental/socket @@ -419,6 +419,8 @@ inline namespace v1 noexcept { return __f1 = (__f1 ^ __f2); } + // TODO define socket_base static constants in .so for C++14 mode + #if _GLIBCXX_HAVE_UNISTD_H class __socket_impl diff --git a/libstdc++-v3/testsuite/experimental/net/internet/socket/opt.cc b/libstdc++-v3/testsuite/experimental/net/internet/socket/opt.cc new file mode 100644 index 00000000000..68bac84a8b1 --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/net/internet/socket/opt.cc @@ -0,0 +1,151 @@ +// { dg-do run { target c++14 } } + +#include +#include +#include + +using namespace std; + +template +void check_gettable_sockopt(P p, C c = C()) +{ + static_assert( is_same().level(p)), int>(), "" ); + static_assert( noexcept(declval().level(p)), "" ); + + static_assert( is_same().name(p)), int>(), "" ); + static_assert( noexcept(declval().name(p)), "" ); + + static_assert( is_same().data(p)), void*>(), "" ); + static_assert( noexcept(declval().data(p)), "" ); + + static_assert( is_same().size(p)), size_t>(), "" ); + static_assert( noexcept(declval().size(p)), "" ); + + static_assert( is_same().resize(p, 0)), void>(), "" ); + static_assert( ! noexcept(declval().resize(p, 0)), "" ); + + VERIFY(c.size(p) == sizeof(T)); +} + +template +void check_settable_sockopt(P p, C c = C()) +{ + static_assert( is_same().level(p)), int>(), "" ); + static_assert( noexcept(declval().level(p)), "" ); + + static_assert( is_same().name(p)), int>(), "" ); + static_assert( noexcept(declval().name(p)), "" ); + + static_assert( is_same().data(p)), const void*>(), "" ); + static_assert( noexcept(declval().data(p)), "" ); + + static_assert( is_same().size(p)), size_t>(), "" ); + static_assert( noexcept(declval().size(p)), "" ); + + VERIFY(c.size(p) == sizeof(T)); +} + +template +void check_boolean_sockopt() +{ + namespace ip = std::experimental::net::ip; + check_gettable_sockopt(ip::tcp::v4()); + check_settable_sockopt(ip::tcp::v4()); + + static_assert( is_destructible(), "" ); + static_assert( is_nothrow_default_constructible(), "" ); + static_assert( is_nothrow_copy_constructible(), "" ); + static_assert( is_nothrow_copy_assignable(), "" ); + + static_assert( is_nothrow_constructible(), "" ); + static_assert( is_nothrow_assignable(), "" ); + + static_assert( is_same().value()), bool>(), "" ); + static_assert( noexcept(declval().value()), "" ); + + static_assert( is_same(declval())), bool>(), "" ); + static_assert( noexcept(static_cast(declval())), "" ); + + static_assert( is_same()), bool>(), "" ); + static_assert( noexcept(!declval()), "" ); +} + +template +void check_integer_sockopt() +{ + namespace ip = std::experimental::net::ip; + check_gettable_sockopt(ip::tcp::v4()); + check_settable_sockopt(ip::tcp::v4()); + + static_assert( is_destructible(), "" ); + static_assert( is_nothrow_default_constructible(), "" ); + static_assert( is_nothrow_copy_constructible(), "" ); + static_assert( is_nothrow_copy_assignable(), "" ); + + static_assert( is_nothrow_constructible(), "" ); + static_assert( is_nothrow_assignable(), "" ); + + static_assert( is_same().value()), int>(), "" ); + static_assert( noexcept(declval().value()), "" ); +} + +template +void check_mcast_sockopt(C& c) +{ + namespace ip = std::experimental::net::ip; + static_assert( is_destructible(), "" ); + static_assert( is_copy_constructible(), "" ); + static_assert( is_copy_assignable(), "" ); + + check_settable_sockopt(ip::tcp::v6(), c); + check_settable_sockopt(ip::tcp::v4(), c); + + static_assert( is_nothrow_constructible(), "" ); + static_assert( ! is_convertible(), "explicit" ); + static_assert( is_nothrow_constructible(), "" ); + static_assert( ! is_convertible(), "explicit" ); + static_assert( is_nothrow_constructible(), "" ); + static_assert( is_nothrow_constructible(), "" ); + static_assert( ! is_convertible(), "explicit" ); + static_assert( is_nothrow_constructible(), "" ); +} + +void test_option_types() +{ + namespace ip = std::experimental::net::ip; +#if __has_include() + check_boolean_sockopt(); + + check_boolean_sockopt(); + + check_integer_sockopt(); + + ip::multicast::join_group join(ip::address_v4::any()); + check_mcast_sockopt(join); + + ip::multicast::leave_group leave(ip::address_v4::any()); + check_mcast_sockopt(leave); + + using outbound_if = ip::multicast::outbound_interface; + outbound_if oif(ip::address_v4::any()); + check_settable_sockopt(ip::tcp::v6(), oif); + check_settable_sockopt(ip::tcp::v4(), oif); + static_assert( is_destructible(), "" ); + static_assert( ! is_default_constructible(), "" ); + static_assert( is_nothrow_copy_constructible(), "" ); + static_assert( is_nothrow_copy_assignable(), "" ); + static_assert( is_nothrow_constructible(), "" ); + static_assert( ! is_convertible(), "explicit" ); + static_assert( is_nothrow_constructible(), "" ); + static_assert( ! is_convertible(), "explicit" ); + + check_integer_sockopt(); + + check_boolean_sockopt(); +#endif +} + +int main() +{ + test_option_types(); +} diff --git a/libstdc++-v3/testsuite/experimental/net/socket/socket_base.cc b/libstdc++-v3/testsuite/experimental/net/socket/socket_base.cc index 95cd8151840..1c02c5a09da 100644 --- a/libstdc++-v3/testsuite/experimental/net/socket/socket_base.cc +++ b/libstdc++-v3/testsuite/experimental/net/socket/socket_base.cc @@ -24,6 +24,12 @@ using S = std::experimental::net::socket_base; using namespace std; +static_assert( ! is_default_constructible(), "protected" ); +static_assert( ! is_destructible(), "protected" ); +struct Sock : S { }; +static_assert( is_default_constructible(), "" ); +static_assert( is_destructible(), "" ); + // Dummy protocol struct P { @@ -34,9 +40,6 @@ struct P }; }; -static_assert( ! is_default_constructible(), "" ); -static_assert( ! is_destructible(), "" ); - template void check_gettable_sockopt() { @@ -92,6 +95,7 @@ void check_boolean_sockopt() static_assert( is_nothrow_copy_assignable(), "" ); static_assert( is_nothrow_constructible(), "" ); + static_assert( ! is_convertible(), "constructor is explicit" ); static_assert( is_nothrow_assignable(), "" ); static_assert( is_same().value()), bool>(), "" ); @@ -116,6 +120,7 @@ void check_integer_sockopt() static_assert( is_nothrow_copy_assignable(), "" ); static_assert( is_nothrow_constructible(), "" ); + static_assert( ! is_convertible(), "constructor is explicit" ); static_assert( is_nothrow_assignable(), "" ); static_assert( is_same().value()), int>(), "" ); @@ -124,6 +129,7 @@ void check_integer_sockopt() void test_option_types() { +#if __has_include() check_boolean_sockopt(); check_boolean_sockopt(); @@ -163,10 +169,12 @@ void test_option_types() check_integer_sockopt(); check_integer_sockopt(); +#endif } void test_constants() { +#if __has_include() static_assert( is_enum::value, "" ); static_assert( S::shutdown_receive != S::shutdown_send, "" ); static_assert( S::shutdown_receive != S::shutdown_both, "" ); @@ -183,6 +191,7 @@ void test_constants() auto m = &S::max_listen_connections; static_assert( is_same::value, "" ); +#endif } int main()