libstdc++: Fix socket option classes
authorJonathan Wakely <jwakely@redhat.com>
Mon, 26 Apr 2021 20:16:21 +0000 (21:16 +0100)
committerJonathan Wakely <jwakely@redhat.com>
Mon, 26 Apr 2021 20:16:21 +0000 (21:16 +0100)
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.

libstdc++-v3/include/experimental/bits/net.h
libstdc++-v3/include/experimental/socket
libstdc++-v3/testsuite/experimental/net/socket/socket_base.cc

index 0ac9ca7ddc68a5685ec1ccb4fddb7b0e2f7fca6d..3087f8957daee63cb5b21ff273bdad886a3f25d4 100644 (file)
@@ -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);
index 09c3b729607a72488ead8beeee2492d5c618ffd1..538dc78e72c3b3899cb2d273aed8c60ca92ebc44 100644 (file)
@@ -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;
     };
index b0b02b4e560af1fa9d8f973b1bcd0b4a4bb22c49..95cd8151840e36c954a9b328be2dc1bec5a16a89 100644 (file)
 // 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();
+}