public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
* [committed 1/2] libstdc++: Fix socket option classes
@ 2021-04-26 20:20 Jonathan Wakely
  2021-04-26 20:22 ` [committed 2/2] libstdc++: Fix internet " Jonathan Wakely
  0 siblings, 1 reply; 2+ messages in thread
From: Jonathan Wakely @ 2021-04-26 20:20 UTC (permalink / raw)
  To: libstdc++, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1488 bytes --]

This fixes some flaws in the socket option types defined in
net::socket_base:

- The constructors were not noexcept.
- The __sockopt_base<T>::value() member function was present
  unconditionally (so was defined for socket_base::linger which is
  incorrect).
- The __socket_crtp<C, T>::operator=(T) assignment operator was not
  noexcept, and was hidden in the derived classes.

Also:

- 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/bits/net.h (__socket_base): Add
	bool template parameter to allow BooleanSocketOption and
	IntegerSocketOption to have different __socket_base<int>
	base classes.
	(__socket_base<bool>): Adjust base class.
	(__socket_base<int>): Add partial specialization.
	(__socket_crtp::operator=(_Tp)): Add noexcept-specifier.
	* include/experimental/socket (socket_base::broadcast)
	(socket_base::debug, socket_base::do_not_route)
	(socket_base::keep_alive, socket_base::linger)
	(socket_base::out_of_band_inline)
	(socket_base::receive_buffer_size)
	(socket_base::receive_low_watermark)
	(socket_base::reuse_address, socket_base::send_buffer_size)
	(socket_base::send_low_watermark): Add using-declaration for
	__socket_crtp::operator=(_Tp).
	* testsuite/experimental/net/socket/socket_base.cc: Check
	properties of socket option types.

Tested powerpc64le-linux and powerpc-aix. Committed to trunk.


[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 15263 bytes --]

commit 06c86a4f210c76a157512a2963e6c31302d161cb
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Mon Apr 26 21:16:21 2021

    libstdc++: Fix socket option classes
    
    This fixes some flaws in the socket option types defined in
    net::socket_base:
    
    - The constructors were not noexcept.
    - The __sockopt_base<T>::value() member function was present
      unconditionally (so was defined for socket_base::linger which is
      incorrect).
    - The __socket_crtp<C, T>::operator=(T) assignment operator was not
      noexcept, and was hidden in the derived classes.
    
    Also:
    
    - 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/bits/net.h (__socket_base): Add
            bool template parameter to allow BooleanSocketOption and
            IntegerSocketOption to have different __socket_base<int>
            base classes.
            (__socket_base<bool>): Adjust base class.
            (__socket_base<int>): Add partial specialization.
            (__socket_crtp::operator=(_Tp)): Add noexcept-specifier.
            * include/experimental/socket (socket_base::broadcast)
            (socket_base::debug, socket_base::do_not_route)
            (socket_base::keep_alive, socket_base::linger)
            (socket_base::out_of_band_inline)
            (socket_base::receive_buffer_size)
            (socket_base::receive_low_watermark)
            (socket_base::reuse_address, socket_base::send_buffer_size)
            (socket_base::send_low_watermark): Add using-declaration for
            __socket_crtp::operator=(_Tp).
            * testsuite/experimental/net/socket/socket_base.cc: Check
            properties of socket option types.

diff --git a/libstdc++-v3/include/experimental/bits/net.h b/libstdc++-v3/include/experimental/bits/net.h
index 0ac9ca7ddc6..3087f8957da 100644
--- a/libstdc++-v3/include/experimental/bits/net.h
+++ b/libstdc++-v3/include/experimental/bits/net.h
@@ -95,15 +95,20 @@ inline namespace v1
 
   /// @endcond
 
-  // Base class for types meeting IntegerSocketOption requirements.
-  template<typename _Tp>
+  // Base class for types meeting both GettableSocketOption and
+  // SettableSocketOption requirements.
+  // The bool parameter allows __sockopt_base<bool> to have a
+  // __sockopt_base<int, B> base class (so that its _M_value is an int)
+  // but to have that be a distinct type from __sockopt_base<int>.
+  template<typename _Tp, bool = true>
     struct __sockopt_base
     {
       __sockopt_base() = default;
 
-      explicit __sockopt_base(int __val) : _M_value(__val) { }
-
-      int value() const noexcept { return _M_value; }
+      explicit
+      __sockopt_base(_Tp __val) noexcept(noexcept(_Tp(std::declval<_Tp&>())))
+      : _M_value(__val)
+      { }
 
       template<typename _Protocol>
 	void*
@@ -134,24 +139,36 @@ inline namespace v1
 
   // Base class for types meeting BooleanSocketOption requirements.
   template<>
-    struct __sockopt_base<bool> : __sockopt_base<int>
+    struct __sockopt_base<bool> : __sockopt_base<int, false>
     {
       __sockopt_base() = default;
 
-      explicit __sockopt_base(bool __val) : __sockopt_base<int>(__val) { }
+      explicit
+      __sockopt_base(bool __val) noexcept
+      : __sockopt_base<int, false>(__val)
+      { }
 
-      bool value() const noexcept { return __sockopt_base<int>::_M_value; }
+      bool value() const noexcept { return this->_M_value; }
       explicit operator bool() const noexcept { return value(); }
       bool operator!() const noexcept { return !value(); }
     };
 
+  // Base class for types meeting IntegerSocketOption requirements.
+  template<>
+    struct __sockopt_base<int> : __sockopt_base<int, false>
+    {
+      using __sockopt_base<int, false>::__sockopt_base;
+
+      int value() const noexcept { return this->_M_value; }
+    };
+
   template<typename _Derived, typename _Tp = int>
     struct __sockopt_crtp : __sockopt_base<_Tp>
     {
       using __sockopt_base<_Tp>::__sockopt_base;
 
       _Derived&
-      operator=(_Tp __value)
+      operator=(_Tp __value) noexcept(noexcept(__value = __value))
       {
 	__sockopt_base<_Tp>::_M_value = __value;
 	return static_cast<_Derived&>(*this);
diff --git a/libstdc++-v3/include/experimental/socket b/libstdc++-v3/include/experimental/socket
index 09c3b729607..538dc78e72c 100644
--- a/libstdc++-v3/include/experimental/socket
+++ b/libstdc++-v3/include/experimental/socket
@@ -138,41 +138,59 @@ inline namespace v1
   {
   public:
 #ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
-    struct broadcast : __sockopt_crtp<broadcast, bool>
+    class broadcast : public __sockopt_crtp<broadcast, bool>
     {
+    public:
       using __sockopt_crtp::__sockopt_crtp;
+      using __sockopt_crtp::operator=;
 
+    private:
+      friend __sockopt_crtp<broadcast, bool>;
       static const int _S_level = SOL_SOCKET;
       static const int _S_name = SO_BROADCAST;
     };
 
-    struct debug : __sockopt_crtp<debug, bool>
+    class debug : public __sockopt_crtp<debug, bool>
     {
+    public:
+      friend __sockopt_crtp<debug, bool>;
       using __sockopt_crtp::__sockopt_crtp;
+      using __sockopt_crtp::operator=;
 
+    private:
       static const int _S_level = SOL_SOCKET;
       static const int _S_name = SO_DEBUG;
     };
 
-    struct do_not_route : __sockopt_crtp<do_not_route, bool>
+    class do_not_route : public __sockopt_crtp<do_not_route, bool>
     {
+    public:
       using __sockopt_crtp::__sockopt_crtp;
+      using __sockopt_crtp::operator=;
 
+    private:
+      friend __sockopt_crtp<do_not_route, bool>;
       static const int _S_level = SOL_SOCKET;
       static const int _S_name = SO_DONTROUTE;
     };
 
-    struct keep_alive : __sockopt_crtp<keep_alive, bool>
+    class keep_alive : public __sockopt_crtp<keep_alive, bool>
     {
+    public:
       using __sockopt_crtp::__sockopt_crtp;
+      using __sockopt_crtp::operator=;
 
+    private:
+      friend __sockopt_crtp<keep_alive, bool>;
       static const int _S_level = SOL_SOCKET;
       static const int _S_name = SO_KEEPALIVE;
     };
 
-    struct linger : __sockopt_crtp<linger, ::linger>
+    class linger : public __sockopt_crtp<linger, ::linger>
     {
+    public:
       using __sockopt_crtp::__sockopt_crtp;
+      using __sockopt_crtp::operator=;
 
       linger() noexcept = default;
 
@@ -198,54 +216,80 @@ inline namespace v1
       timeout(chrono::seconds __t) noexcept
       { _M_value.l_linger = __t.count(); }
 
+    private:
+      friend __sockopt_crtp<linger, ::linger>;
       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>
+    class out_of_band_inline : public __sockopt_crtp<out_of_band_inline, bool>
     {
+    public:
       using __sockopt_crtp::__sockopt_crtp;
+      using __sockopt_crtp::operator=;
 
+    private:
+      friend __sockopt_crtp<out_of_band_inline, bool>;
       static const int _S_level = SOL_SOCKET;
       static const int _S_name = SO_OOBINLINE;
     };
 
-    struct receive_buffer_size : __sockopt_crtp<receive_buffer_size>
+    class receive_buffer_size : public __sockopt_crtp<receive_buffer_size>
     {
+    public:
       using __sockopt_crtp::__sockopt_crtp;
+      using __sockopt_crtp::operator=;
 
+    private:
+      friend __sockopt_crtp<receive_buffer_size>;
       static const int _S_level = SOL_SOCKET;
       static const int _S_name = SO_RCVBUF;
     };
 
-    struct receive_low_watermark : __sockopt_crtp<receive_low_watermark>
+    class receive_low_watermark : public __sockopt_crtp<receive_low_watermark>
     {
+    public:
       using __sockopt_crtp::__sockopt_crtp;
+      using __sockopt_crtp::operator=;
 
+    private:
+      friend __sockopt_crtp<receive_low_watermark>;
       static const int _S_level = SOL_SOCKET;
       static const int _S_name = SO_RCVLOWAT;
     };
 
-    struct reuse_address : __sockopt_crtp<reuse_address, bool>
+    class reuse_address : public __sockopt_crtp<reuse_address, bool>
     {
+    public:
       using __sockopt_crtp::__sockopt_crtp;
+      using __sockopt_crtp::operator=;
 
+    private:
+      friend __sockopt_crtp<reuse_address, bool>;
       static const int _S_level = SOL_SOCKET;
       static const int _S_name = SO_REUSEADDR;
     };
 
-    struct send_buffer_size : __sockopt_crtp<send_buffer_size>
+    class send_buffer_size : public __sockopt_crtp<send_buffer_size>
     {
+    public:
       using __sockopt_crtp::__sockopt_crtp;
+      using __sockopt_crtp::operator=;
 
+    private:
+      friend __sockopt_crtp<send_buffer_size>;
       static const int _S_level = SOL_SOCKET;
       static const int _S_name = SO_SNDBUF;
     };
 
-    struct send_low_watermark : __sockopt_crtp<send_low_watermark>
+    class send_low_watermark : public __sockopt_crtp<send_low_watermark>
     {
+    public:
       using __sockopt_crtp::__sockopt_crtp;
+      using __sockopt_crtp::operator=;
 
+    private:
+      friend __sockopt_crtp<send_low_watermark>;
       static const int _S_level = SOL_SOCKET;
       static const int _S_name = SO_SNDLOWAT;
     };
diff --git a/libstdc++-v3/testsuite/experimental/net/socket/socket_base.cc b/libstdc++-v3/testsuite/experimental/net/socket/socket_base.cc
index b0b02b4e560..95cd8151840 100644
--- a/libstdc++-v3/testsuite/experimental/net/socket/socket_base.cc
+++ b/libstdc++-v3/testsuite/experimental/net/socket/socket_base.cc
@@ -15,14 +15,156 @@
 // with this library; see the file COPYING3.  If not see
 // <http://www.gnu.org/licenses/>.
 
-// { dg-do compile { target c++14 } }
+// { dg-do run { target c++14 } }
 
 #include <experimental/socket>
 #include <testsuite_common_types.h>
+#include <testsuite_hooks.h>
 
 using S = std::experimental::net::socket_base;
 using namespace std;
 
+// Dummy protocol
+struct P
+{
+  struct endpoint
+  {
+    using protocol_type = P;
+    P protocol() const;
+  };
+};
+
+static_assert( ! is_default_constructible<S>(), "" );
+static_assert( ! is_destructible<S>(), "" );
+
+template<typename C, typename T>
+void check_gettable_sockopt()
+{
+  P p;
+  static_assert( is_same<decltype(declval<const C&>().level(p)), int>(), "" );
+  static_assert( noexcept(declval<const C&>().level(p)), "" );
+
+  static_assert( is_same<decltype(declval<const C&>().name(p)), int>(), "" );
+  static_assert( noexcept(declval<const C&>().name(p)), "" );
+
+  static_assert( is_same<decltype(declval<C&>().data(p)), void*>(), "" );
+  static_assert( noexcept(declval<C&>().data(p)), "" );
+
+  static_assert( is_same<decltype(declval<const C&>().size(p)), size_t>(), "" );
+  static_assert( noexcept(declval<const C&>().size(p)), "" );
+
+  static_assert( is_same<decltype(declval<C&>().resize(p, 0)), void>(), "" );
+  static_assert( ! noexcept(declval<C&>().resize(p, 0)), "" );
+
+  C opt;
+  VERIFY(opt.size(p) == sizeof(T));
+}
+
+template<typename C, typename T>
+void check_settable_sockopt()
+{
+  P p;
+  static_assert( is_same<decltype(declval<const C&>().level(p)), int>(), "" );
+  static_assert( noexcept(declval<const C&>().level(p)), "" );
+
+  static_assert( is_same<decltype(declval<const C&>().name(p)), int>(), "" );
+  static_assert( noexcept(declval<const C&>().name(p)), "" );
+
+  static_assert( is_same<decltype(declval<const C&>().data(p)), const void*>(), "" );
+  static_assert( noexcept(declval<const C&>().data(p)), "" );
+
+  static_assert( is_same<decltype(declval<C&>().size(p)), size_t>(), "" );
+  static_assert( noexcept(declval<C&>().size(p)), "" );
+
+  C opt;
+  VERIFY(opt.size(p) == sizeof(T));
+}
+
+template<typename C, typename T = int>
+void check_boolean_sockopt()
+{
+  check_gettable_sockopt<C, T>();
+  check_settable_sockopt<C, T>();
+
+  static_assert( is_destructible<C>(), "" );
+  static_assert( is_nothrow_default_constructible<C>(), "" );
+  static_assert( is_nothrow_copy_constructible<C>(), "" );
+  static_assert( is_nothrow_copy_assignable<C>(), "" );
+
+  static_assert( is_nothrow_constructible<C, bool>(), "" );
+  static_assert( is_nothrow_assignable<C&, bool>(), "" );
+
+  static_assert( is_same<decltype(declval<const C&>().value()), bool>(), "" );
+  static_assert( noexcept(declval<const C&>().value()), "" );
+
+  static_assert( is_same<decltype(static_cast<bool>(declval<const C&>())), bool>(), "" );
+  static_assert( noexcept(static_cast<bool>(declval<const C&>())), "" );
+
+  static_assert( is_same<decltype(!declval<const C&>()), bool>(), "" );
+  static_assert( noexcept(!declval<const C&>()), "" );
+}
+
+template<typename C, typename T = int>
+void check_integer_sockopt()
+{
+  check_gettable_sockopt<C, T>();
+  check_settable_sockopt<C, T>();
+
+  static_assert( is_destructible<C>(), "" );
+  static_assert( is_nothrow_default_constructible<C>(), "" );
+  static_assert( is_nothrow_copy_constructible<C>(), "" );
+  static_assert( is_nothrow_copy_assignable<C>(), "" );
+
+  static_assert( is_nothrow_constructible<C, int>(), "" );
+  static_assert( is_nothrow_assignable<C&, int>(), "" );
+
+  static_assert( is_same<decltype(declval<const C&>().value()), int>(), "" );
+  static_assert( noexcept(declval<const C&>().value()), "" );
+}
+
+void test_option_types()
+{
+  check_boolean_sockopt<S::broadcast>();
+
+  check_boolean_sockopt<S::debug>();
+
+  check_boolean_sockopt<S::do_not_route>();
+
+  check_boolean_sockopt<S::keep_alive>();
+
+  check_gettable_sockopt<S::linger, ::linger>();
+  check_settable_sockopt<S::linger, ::linger>();
+  static_assert( is_destructible<S::linger>(), "" );
+  static_assert( is_nothrow_default_constructible<S::linger>(), "" );
+  static_assert( is_nothrow_copy_constructible<S::linger>(), "" );
+  static_assert( is_nothrow_copy_assignable<S::linger>(), "" );
+  static_assert( is_nothrow_constructible<S::linger, bool, chrono::seconds>(), "" );
+
+  static_assert( is_same<decltype(declval<const S::linger&>().enabled()), bool>(), "" );
+  static_assert( noexcept(declval<const S::linger&>().enabled()), "" );
+
+  static_assert( is_void<decltype(declval<S::linger&>().enabled(true))>(), "" );
+  static_assert( noexcept(declval<S::linger&>().enabled(true)), "" );
+
+  static_assert( is_same<decltype(declval<const S::linger&>().timeout()), chrono::seconds>(), "" );
+  static_assert( noexcept(declval<const S::linger&>().timeout()), "" );
+
+  static_assert( is_void<decltype(declval<S::linger&>().timeout(chrono::seconds()))>(), "" );
+  static_assert( noexcept(declval<S::linger&>().timeout(chrono::seconds())), "" );
+
+  check_boolean_sockopt<S::out_of_band_inline>();
+
+  check_integer_sockopt<S::receive_buffer_size>();
+
+  check_integer_sockopt<S::receive_low_watermark>();
+
+  check_boolean_sockopt<S::reuse_address>();
+
+  check_integer_sockopt<S::send_buffer_size>();
+
+  check_integer_sockopt<S::send_low_watermark>();
+}
+
 void test_constants()
 {
   static_assert( is_enum<S::shutdown_type>::value, "" );
@@ -43,3 +185,7 @@ void test_constants()
   static_assert( is_same<decltype(m), const int*>::value, "" );
 }
 
+int main()
+{
+  test_option_types();
+}

^ permalink raw reply	[flat|nested] 2+ messages in thread

* [committed 2/2] libstdc++: Fix internet socket option classes
  2021-04-26 20:20 [committed 1/2] libstdc++: Fix socket option classes Jonathan Wakely
@ 2021-04-26 20:22 ` Jonathan Wakely
  0 siblings, 0 replies; 2+ messages in thread
From: Jonathan Wakely @ 2021-04-26 20:22 UTC (permalink / raw)
  To: libstdc++, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1615 bytes --]

Similar to the previous commit, this fixes various problems with the
socket options classes in the <internet> header:

- The constructors were not noexcept.
- The __sockopt_base<T>::value() member function was present
   unconditionally (so was defined for socket_base::linger which is
   incorrect).
- The __socket_crtp<C, T>::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.



Tested powerpc64le-linux and powerpc-aix. Committed to trunk.



[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 19190 bytes --]

commit 2e0b1c6ce3afe0670b96444c6b955ce184ed0046
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Mon Apr 26 21:16:21 2021

    libstdc++: Fix internet socket option classes
    
    Similar to the previous commit, this fixes various problems with the
    socket options classes in the <internet> header:
    
    - The constructors were not noexcept.
    - The __sockopt_base<T>::value() member function was present
      unconditionally (so was defined for socket_base::linger which is
      incorrect).
    - The __socket_crtp<C, T>::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.

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 <arpa/inet.h>		// inet_ntop
 #endif
 #ifdef _GLIBCXX_HAVE_NETINET_IN_H
-# include <netinet/in.h>	// IPPROTO_IP
+# include <netinet/in.h>	// IPPROTO_IP, IPPROTO_IPV6, in_addr, in6_addr
 #endif
 #ifdef _GLIBCXX_HAVE_NETINET_TCP_H
 # include <netinet/tcp.h>	// TCP_NODELAY
@@ -2078,6 +2078,7 @@ namespace ip
     struct no_delay : __sockopt_crtp<no_delay, bool>
     {
       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<v6_only, bool>
+  class v6_only : public __sockopt_crtp<v6_only, bool>
   {
+  public:
     using __sockopt_crtp::__sockopt_crtp;
+    using __sockopt_crtp::operator=;
 
+  private:
+    friend __sockopt_crtp<v6_only, bool>;
     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<hops>
+    class hops : public __sockopt_crtp<hops>
     {
+    public:
       using __sockopt_crtp::__sockopt_crtp;
+      using __sockopt_crtp::operator=;
 
       template<typename _Protocol>
 	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<typename _Protocol>
 	int
 	level(const _Protocol& __p) const noexcept
 	{ return __p.family() == AF_INET6 ? IPPROTO_IPV6 : IPPROTO_IP; }
 
-      template<typename _Protocol>
-	int
-	name(const _Protocol& __p) const noexcept
-	{
-	  return __p.family() == AF_INET6
-	    ? IPV6_JOIN_GROUP : IP_ADD_MEMBERSHIP;
-	}
-      template<typename _Protocol>
-	void*
-	data(const _Protocol&) noexcept
-	{ return std::addressof(_M_value); }
-
       template<typename _Protocol>
 	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<typename _Protocol>
 	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); }
+
+    private:
+      ipv6_mreq _M_v6 = {};
+      ip_mreq _M_v4 = {};
+    };
+
+    /// Request that a socket joins a multicast group.
+    class join_group : private __mcastopt
+    {
+    public:
+      using __mcastopt::__mcastopt;
+      using __mcastopt::level;
+      using __mcastopt::data;
+      using __mcastopt::size;
 
       template<typename _Protocol>
-	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_JOIN_GROUP;
+	  return IP_ADD_MEMBERSHIP;
 	}
-
-    protected:
-      union
-      {
-	ipv6_mreq _M_v6;
-	ip_mreq _M_v4;
-      } _M_value;
     };
 
     /// Request that a socket leaves a multicast group.
-    struct leave_group
+    class leave_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<typename _Protocol>
-	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<typename _Protocol>
 	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_LEAVE_GROUP;
+	  return IP_DROP_MEMBERSHIP;
 	}
-      template<typename _Protocol>
-	void*
-	data(const _Protocol&) noexcept
-	{ return std::addressof(_M_value); }
-
-      template<typename _Protocol>
-	const void*
-	data(const _Protocol&) const noexcept
-	{ return std::addressof(_M_value); }
-
-      template<typename _Protocol>
-	size_t
-	size(const _Protocol& __p) const noexcept
-	{
-	  return __p.family() == AF_INET6
-	    ? sizeof(_M_value._M_v6) : sizeof(_M_value._M_v4);
-	}
-
-      template<typename _Protocol>
-	void
-	resize(const _Protocol& __p, size_t __s)
-	{
-	  if (__s != size(__p))
-	    __throw_length_error("invalid value for socket option resize");
-	}
-
-    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<typename _Protocol>
 	int
@@ -2326,28 +2314,25 @@ namespace ip
 
       template<typename _Protocol>
 	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<typename _Protocol>
 	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<hops>
+    class hops : public __sockopt_crtp<hops>
     {
+    public:
       using __sockopt_crtp::__sockopt_crtp;
+      using __sockopt_crtp::operator=;
 
       template<typename _Protocol>
 	int
@@ -2364,9 +2349,11 @@ namespace ip
     };
 
     /// Set whether datagrams are delivered back to the local application.
-    struct enable_loopback : __sockopt_crtp<enable_loopback>
+    class enable_loopback : public __sockopt_crtp<enable_loopback, bool>
     {
+    public:
       using __sockopt_crtp::__sockopt_crtp;
+      using __sockopt_crtp::operator=;
 
       template<typename _Protocol>
 	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 <experimental/internet>
+#include <testsuite_common_types.h>
+#include <testsuite_hooks.h>
+
+using namespace std;
+
+template<typename C, typename T, typename P>
+void check_gettable_sockopt(P p, C c = C())
+{
+  static_assert( is_same<decltype(declval<const C&>().level(p)), int>(), "" );
+  static_assert( noexcept(declval<const C&>().level(p)), "" );
+
+  static_assert( is_same<decltype(declval<const C&>().name(p)), int>(), "" );
+  static_assert( noexcept(declval<const C&>().name(p)), "" );
+
+  static_assert( is_same<decltype(declval<C&>().data(p)), void*>(), "" );
+  static_assert( noexcept(declval<C&>().data(p)), "" );
+
+  static_assert( is_same<decltype(declval<const C&>().size(p)), size_t>(), "" );
+  static_assert( noexcept(declval<const C&>().size(p)), "" );
+
+  static_assert( is_same<decltype(declval<C&>().resize(p, 0)), void>(), "" );
+  static_assert( ! noexcept(declval<C&>().resize(p, 0)), "" );
+
+  VERIFY(c.size(p) == sizeof(T));
+}
+
+template<typename C, typename T, typename P>
+void check_settable_sockopt(P p, C c = C())
+{
+  static_assert( is_same<decltype(declval<const C&>().level(p)), int>(), "" );
+  static_assert( noexcept(declval<const C&>().level(p)), "" );
+
+  static_assert( is_same<decltype(declval<const C&>().name(p)), int>(), "" );
+  static_assert( noexcept(declval<const C&>().name(p)), "" );
+
+  static_assert( is_same<decltype(declval<const C&>().data(p)), const void*>(), "" );
+  static_assert( noexcept(declval<const C&>().data(p)), "" );
+
+  static_assert( is_same<decltype(declval<C&>().size(p)), size_t>(), "" );
+  static_assert( noexcept(declval<C&>().size(p)), "" );
+
+  VERIFY(c.size(p) == sizeof(T));
+}
+
+template<typename C, typename T = int>
+void check_boolean_sockopt()
+{
+  namespace ip = std::experimental::net::ip;
+  check_gettable_sockopt<C, T>(ip::tcp::v4());
+  check_settable_sockopt<C, T>(ip::tcp::v4());
+
+  static_assert( is_destructible<C>(), "" );
+  static_assert( is_nothrow_default_constructible<C>(), "" );
+  static_assert( is_nothrow_copy_constructible<C>(), "" );
+  static_assert( is_nothrow_copy_assignable<C>(), "" );
+
+  static_assert( is_nothrow_constructible<C, bool>(), "" );
+  static_assert( is_nothrow_assignable<C&, bool>(), "" );
+
+  static_assert( is_same<decltype(declval<const C&>().value()), bool>(), "" );
+  static_assert( noexcept(declval<const C&>().value()), "" );
+
+  static_assert( is_same<decltype(static_cast<bool>(declval<const C&>())), bool>(), "" );
+  static_assert( noexcept(static_cast<bool>(declval<const C&>())), "" );
+
+  static_assert( is_same<decltype(!declval<const C&>()), bool>(), "" );
+  static_assert( noexcept(!declval<const C&>()), "" );
+}
+
+template<typename C, typename T = int>
+void check_integer_sockopt()
+{
+  namespace ip = std::experimental::net::ip;
+  check_gettable_sockopt<C, T>(ip::tcp::v4());
+  check_settable_sockopt<C, T>(ip::tcp::v4());
+
+  static_assert( is_destructible<C>(), "" );
+  static_assert( is_nothrow_default_constructible<C>(), "" );
+  static_assert( is_nothrow_copy_constructible<C>(), "" );
+  static_assert( is_nothrow_copy_assignable<C>(), "" );
+
+  static_assert( is_nothrow_constructible<C, int>(), "" );
+  static_assert( is_nothrow_assignable<C&, int>(), "" );
+
+  static_assert( is_same<decltype(declval<const C&>().value()), int>(), "" );
+  static_assert( noexcept(declval<const C&>().value()), "" );
+}
+
+template<typename C>
+void check_mcast_sockopt(C& c)
+{
+  namespace ip = std::experimental::net::ip;
+  static_assert( is_destructible<C>(), "" );
+  static_assert( is_copy_constructible<C>(), "" );
+  static_assert( is_copy_assignable<C>(), "" );
+
+  check_settable_sockopt<C, ipv6_mreq>(ip::tcp::v6(), c);
+  check_settable_sockopt<C, ip_mreq>(ip::tcp::v4(), c);
+
+  static_assert( is_nothrow_constructible<C, const ip::address&>(), "" );
+  static_assert( ! is_convertible<const ip::address&, C>(), "explicit" );
+  static_assert( is_nothrow_constructible<C, const ip::address_v4&>(), "" );
+  static_assert( ! is_convertible<const ip::address_v4&, C>(), "explicit" );
+  static_assert( is_nothrow_constructible<C, const ip::address_v4&, const ip::address_v4&>(), "" );
+  static_assert( is_nothrow_constructible<C, const ip::address_v6&>(), "" );
+  static_assert( ! is_convertible<const ip::address_v6&, C>(), "explicit" );
+  static_assert( is_nothrow_constructible<C, const ip::address_v6&, unsigned>(), "" );
+}
+
+void test_option_types()
+{
+  namespace ip = std::experimental::net::ip;
+#if __has_include(<netinet/in.h>)
+  check_boolean_sockopt<ip::tcp::no_delay>();
+
+  check_boolean_sockopt<ip::v6_only>();
+
+  check_integer_sockopt<ip::unicast::hops>();
+
+  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<outbound_if, unsigned>(ip::tcp::v6(), oif);
+  check_settable_sockopt<outbound_if, ::in_addr>(ip::tcp::v4(), oif);
+  static_assert( is_destructible<outbound_if>(), "" );
+  static_assert( ! is_default_constructible<outbound_if>(), "" );
+  static_assert( is_nothrow_copy_constructible<outbound_if>(), "" );
+  static_assert( is_nothrow_copy_assignable<outbound_if>(), "" );
+  static_assert( is_nothrow_constructible<outbound_if, const ip::address_v4&>(), "" );
+  static_assert( ! is_convertible<const ip::address_v4&, outbound_if>(), "explicit" );
+  static_assert( is_nothrow_constructible<outbound_if, unsigned>(), "" );
+  static_assert( ! is_convertible<unsigned, outbound_if>(), "explicit" );
+
+  check_integer_sockopt<ip::multicast::hops>();
+
+  check_boolean_sockopt<ip::multicast::enable_loopback>();
+#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<S>(), "protected" );
+static_assert( ! is_destructible<S>(), "protected" );
+struct Sock : S { };
+static_assert( is_default_constructible<Sock>(), "" );
+static_assert( is_destructible<Sock>(), "" );
+
 // Dummy protocol
 struct P
 {
@@ -34,9 +40,6 @@ struct P
   };
 };
 
-static_assert( ! is_default_constructible<S>(), "" );
-static_assert( ! is_destructible<S>(), "" );
-
 template<typename C, typename T>
 void check_gettable_sockopt()
 {
@@ -92,6 +95,7 @@ void check_boolean_sockopt()
   static_assert( is_nothrow_copy_assignable<C>(), "" );
 
   static_assert( is_nothrow_constructible<C, bool>(), "" );
+  static_assert( ! is_convertible<bool, C>(), "constructor is explicit" );
   static_assert( is_nothrow_assignable<C&, bool>(), "" );
 
   static_assert( is_same<decltype(declval<const C&>().value()), bool>(), "" );
@@ -116,6 +120,7 @@ void check_integer_sockopt()
   static_assert( is_nothrow_copy_assignable<C>(), "" );
 
   static_assert( is_nothrow_constructible<C, int>(), "" );
+  static_assert( ! is_convertible<int, C>(), "constructor is explicit" );
   static_assert( is_nothrow_assignable<C&, int>(), "" );
 
   static_assert( is_same<decltype(declval<const C&>().value()), int>(), "" );
@@ -124,6 +129,7 @@ void check_integer_sockopt()
 
 void test_option_types()
 {
+#if __has_include(<socket.h>)
   check_boolean_sockopt<S::broadcast>();
 
   check_boolean_sockopt<S::debug>();
@@ -163,10 +169,12 @@ void test_option_types()
   check_integer_sockopt<S::send_buffer_size>();
 
   check_integer_sockopt<S::send_low_watermark>();
+#endif
 }
 
 void test_constants()
 {
+#if __has_include(<socket.h>)
   static_assert( is_enum<S::shutdown_type>::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<decltype(m), const int*>::value, "" );
+#endif
 }
 
 int main()

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2021-04-26 20:22 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-26 20:20 [committed 1/2] libstdc++: Fix socket option classes Jonathan Wakely
2021-04-26 20:22 ` [committed 2/2] libstdc++: Fix internet " Jonathan Wakely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).