diff mbox series

[committed,1/2] libstdc++: Fix socket option classes

Message ID YIcgmKtqoglIsyud@redhat.com
State New
Headers show
Series [committed,1/2] libstdc++: Fix socket option classes | expand

Commit Message

Jonathan Wakely April 26, 2021, 8:20 p.m. UTC
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.
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 mbox series

Patch

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();
+}