libstdc++: Fix new <sstream> constructors
authorJonathan Wakely <jwakely@redhat.com>
Thu, 5 Nov 2020 12:16:13 +0000 (12:16 +0000)
committerJonathan Wakely <jwakely@redhat.com>
Thu, 5 Nov 2020 13:31:32 +0000 (13:31 +0000)
- Add a missing 'explicit' to a basic_stringbuf constructor.
- Set up the get/put area pointers in the constructor from strings using
  different allocator types.
- Remove public basic_stringbuf::__sv_type alias.
- Do not construct temporary basic_string objects with a
  default-constructed allocator.

Also, change which basic_string constructor is used, as a minor
compile-time optimization. Constructing from a basic_string_view
requires more work from the compiler, so just use a pointer and length.

libstdc++-v3/ChangeLog:

* include/std/sstream (basic_stringbuf(const allocator_type&):
Add explicit.
(basic_stringbuf(const basic_string<C,T,SA>&, openmode, const A&)):
Call _M_stringbuf_init. Construct _M_string from pointer and length
to avoid constraint checks for string view.
(basic_stringbuf::view()): Make __sv_type alias local to the
function.
(basic_istringstream(const basic_string<C,T,SA>&, openmode, const A&)):
Pass string to _M_streambuf instead of constructing a temporary
with the wrong allocator.
(basic_ostringstream(const basic_string<C,T,SA>&, openmode, const A&)):
Likewise.
(basic_stringstream(const basic_string<C,T,SA>&, openmode, const A&)):
Likewise.
* src/c++20/sstream-inst.cc: Use string_view and wstring_view
typedefs in explicit instantiations.
* testsuite/27_io/basic_istringstream/cons/char/1.cc: Add more
tests for constructors.
* testsuite/27_io/basic_ostringstream/cons/char/1.cc: Likewise.
* testsuite/27_io/basic_stringbuf/cons/char/1.cc: Likewise.
* testsuite/27_io/basic_stringbuf/cons/char/2.cc: Likewise.
* testsuite/27_io/basic_stringbuf/cons/wchar_t/1.cc: Likewise.
* testsuite/27_io/basic_stringbuf/cons/wchar_t/2.cc: Likewise.
* testsuite/27_io/basic_stringstream/cons/char/1.cc: Likewise.

libstdc++-v3/include/std/sstream
libstdc++-v3/src/c++20/sstream-inst.cc
libstdc++-v3/testsuite/27_io/basic_istringstream/cons/char/1.cc
libstdc++-v3/testsuite/27_io/basic_ostringstream/cons/char/1.cc
libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/char/1.cc
libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/char/2.cc
libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/wchar_t/1.cc
libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/wchar_t/2.cc
libstdc++-v3/testsuite/27_io/basic_stringstream/cons/char/1.cc

index 276badfd965739b455a94356687e80454d9f3e32..437e2ba2a5f8f593aad3b7141ae168d2f33673f4 100644 (file)
@@ -166,8 +166,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
 #endif
 
 #if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI
-      using __sv_type = basic_string_view<char_type, traits_type>;
-
+      explicit
       basic_stringbuf(const allocator_type& __a)
       : basic_stringbuf(ios_base::in | std::ios_base::out, __a)
       { }
@@ -185,18 +184,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       { _M_stringbuf_init(__mode); }
 
       template<typename _SAlloc>
-      basic_stringbuf(const basic_string<_CharT, _Traits, _SAlloc>& __s,
-                     const allocator_type& __a)
-      : basic_stringbuf(__s, ios_base::in | std::ios_base::out, __a)
-      { }
+       basic_stringbuf(const basic_string<_CharT, _Traits, _SAlloc>& __s,
+                       const allocator_type& __a)
+       : basic_stringbuf(__s, ios_base::in | std::ios_base::out, __a)
+       { }
 
       template<typename _SAlloc>
-      basic_stringbuf(const basic_string<_CharT, _Traits, _SAlloc>& __s,
-                     ios_base::openmode __mode,
-                     const allocator_type& __a)
-      : __streambuf_type(), _M_mode(__mode),
-      _M_string(static_cast<__sv_type>(__s), __a)
-      { }
+       basic_stringbuf(const basic_string<_CharT, _Traits, _SAlloc>& __s,
+                       ios_base::openmode __mode,
+                       const allocator_type& __a)
+       : __streambuf_type(), _M_mode(__mode),
+         _M_string(__s.data(), __s.size(), __a)
+       { _M_stringbuf_init(__mode); }
 
       template<typename _SAlloc>
        explicit
@@ -258,9 +257,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       }
 
 #if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI
-      __sv_type
+      basic_string_view<char_type, traits_type>
       view() const noexcept
       {
+       using __sv_type = basic_string_view<char_type, traits_type>;
+
        if (this->pptr())
          {
            // The current egptr() may not be the actual string end.
@@ -598,9 +599,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
        basic_istringstream(const basic_string<_CharT, _Traits, _SAlloc>& __str,
                            ios_base::openmode __mode,
                            const allocator_type& __a)
-       : __istream_type(),
-       _M_stringbuf(__string_type(__str.data(), __str.size()),
-                    __mode | ios_base::in, __a)
+       : __istream_type(), _M_stringbuf(__str, __mode | ios_base::in, __a)
        { this->init(std::__addressof(_M_stringbuf)); }
 
       template<typename _SAlloc>
@@ -796,9 +795,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
        basic_ostringstream(const basic_string<_CharT, _Traits, _SAlloc>& __str,
                            ios_base::openmode __mode,
                            const allocator_type& __a)
-       : __ostream_type(),
-       _M_stringbuf(__string_type(__str.data(), __str.size()),
-                    __mode | ios_base::out, __a)
+       : __ostream_type(), _M_stringbuf(__str, __mode | ios_base::out, __a)
        { this->init(std::__addressof(_M_stringbuf)); }
 
       template<typename _SAlloc>
@@ -991,8 +988,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
        basic_stringstream(const basic_string<_CharT, _Traits, _SAlloc>& __str,
                           ios_base::openmode __mode,
                           const allocator_type& __a)
-       : __iostream_type(),
-       _M_stringbuf(__string_type(__str.data(), __str.size()), __mode, __a)
+       : __iostream_type(), _M_stringbuf(__str, __mode, __a)
        { this->init(std::__addressof(_M_stringbuf)); }
 
       template<typename _SAlloc>
index 8c6840115c507c3b12f80157832be9788e0b3aab..ada3eabac1f552b50f06a6be0ecaac1f6ebc0c1f 100644 (file)
@@ -43,7 +43,7 @@ template basic_stringbuf<char>::basic_stringbuf(basic_stringbuf&&,
                                                const allocator_type&);
 template basic_stringbuf<char>::allocator_type
 basic_stringbuf<char>::get_allocator() const noexcept;
-template basic_stringbuf<char>::__sv_type
+template string_view
 basic_stringbuf<char>::view() const noexcept;
 
 template basic_istringstream<char>::basic_istringstream(ios_base::openmode,
@@ -68,8 +68,6 @@ template string_view
 basic_stringstream<char>::view() const noexcept;
 
 #ifdef _GLIBCXX_USE_WCHAR_T
-using wsv_type = basic_string_view<wchar_t>;
-
 template basic_stringbuf<wchar_t>::basic_stringbuf(const allocator_type&);
 template basic_stringbuf<wchar_t>::basic_stringbuf(ios_base::openmode,
                                                   const allocator_type&);
@@ -80,7 +78,7 @@ template basic_stringbuf<wchar_t>::basic_stringbuf(basic_stringbuf&&,
 template basic_stringbuf<wchar_t>::allocator_type
 basic_stringbuf<wchar_t>::get_allocator() const noexcept;
 
-template basic_istringstream<wchar_t>::__stringbuf_type::__sv_type
+template wstring_view
 basic_stringbuf<wchar_t>::view() const noexcept;
 
 template basic_istringstream<wchar_t>::basic_istringstream(ios_base::openmode,
index 496aa6963e0b2a77006e92cb13fb266302be2375..6584b88193d3a7dbb3616506efc5f05658109e15 100644 (file)
@@ -33,7 +33,7 @@ test01()
   std::istringstream stm(std::ios_base::in, a);
 }
 
-auto const cstr = "This is a test";
+auto const cstr = "This is a test string";
 
 void
 test02()
@@ -75,11 +75,44 @@ test03()
   }
 }
 
+// A minimal allocator with no default constructor
+template<typename T>
+  struct NoDefaultCons : __gnu_test::SimpleAllocator<T>
+  {
+    using __gnu_test::SimpleAllocator<T>::SimpleAllocator;
+
+    NoDefaultCons() = delete;
+
+    NoDefaultCons(int) { }
+  };
+
+void
+test04()
+{
+  using sstream = std::basic_istringstream<char, std::char_traits<char>,
+                                          NoDefaultCons<char>>;
+
+  NoDefaultCons<char> a(1);
+  const std::string str(cstr);
+
+  sstream ss1(str, a);
+  VERIFY( ss1.str() == cstr );
+  VERIFY( ss1.get() == cstr[0] );
+
+  sstream ss2(str, std::ios::out, a);
+  VERIFY( ss2.str() == cstr );
+  VERIFY( ss2.get() == cstr[0] );
+
+  sstream ss3(std::string(str), std::ios::out, a);
+  VERIFY( ss3.str() == cstr );
+  VERIFY( ss3.get() == cstr[0] );
+}
+
 int
 main()
 {
   test01();
   test02();
   test03();
-  return 0;
+  test04();
 }
index cba69f80931ad0da4f40bd38e757a78dc20129fa..885949caf002324ed246860460f6c31a4682de4f 100644 (file)
@@ -33,7 +33,7 @@ test01()
   std::ostringstream stm(std::ios_base::in, a);
 }
 
-auto const cstr = "This is a test";
+auto const cstr = "This is a test string";
 
 void
 test02()
@@ -75,11 +75,45 @@ test03()
   }
 }
 
+// A minimal allocator with no default constructor
+template<typename T>
+  struct NoDefaultCons : __gnu_test::SimpleAllocator<T>
+  {
+    using __gnu_test::SimpleAllocator<T>::SimpleAllocator;
+
+    NoDefaultCons() = delete;
+
+    NoDefaultCons(int) { }
+  };
+
+void
+test04()
+{
+  using sstream = std::basic_ostringstream<char, std::char_traits<char>,
+                                          NoDefaultCons<char>>;
+
+  NoDefaultCons<char> a(1);
+  const std::string str(cstr);
+
+  sstream ss1(str, a);
+  VERIFY( ss1.str() == cstr );
+
+  sstream ss2(str, std::ios::in, a);
+  VERIFY( ss2.str() == cstr );
+  VERIFY( bool(ss2 << "That") );
+  VERIFY( ss2.str() == "That is a test string" );
+
+  sstream ss3(std::string(str), std::ios::ate, a);
+  VERIFY( ss3.str() == cstr );
+  VERIFY( bool(ss3 << "y thing") );
+  VERIFY( ss3.str() == "This is a test stringy thing" );
+}
+
 int
 main()
 {
   test01();
   test02();
   test03();
-  return 0;
+  test04();
 }
index 0e9649798f88ef48fac1c183a3cf8a0b8447800c..bd17e6ddc9e3619e3ce7cb1339c8094d544c7d43 100644 (file)
@@ -17,7 +17,7 @@
 // with this library; see the file COPYING3.  If not see
 // <http://www.gnu.org/licenses/>.
 
-// 27.7.1.1  basic_stringbuf constructors  [lib.stringbuf.cons]
+// C++03 27.7.1.1  basic_stringbuf constructors  [lib.stringbuf.cons]
 
 #include <sstream>
 #include <testsuite_hooks.h>
@@ -30,8 +30,41 @@ void test01()
   VERIFY( sbuf.check_pointers() );
 }
 
-int main() 
+void test02()
+{
+  std::stringbuf sbuf;
+  VERIFY( sbuf.str().empty() );
+
+  std::stringbuf sbuf1(std::ios::in);
+  VERIFY( sbuf1.str().empty() );
+
+  const std::string str = "This is my boomstick!";
+
+  std::stringbuf sbuf2(str);
+  VERIFY( sbuf2.str() == str );
+
+  std::stringbuf sbuf3(str, std::ios::in);
+  VERIFY( sbuf3.str() == str );
+  VERIFY( sbuf3.sgetc() == str[0] );
+  VERIFY( sbuf3.sputc('X') == std::stringbuf::traits_type::eof() );
+
+  std::stringbuf sbuf4(str, std::ios::out);
+  VERIFY( sbuf4.str() == str );
+  VERIFY( sbuf4.sputc('Y') == 'Y' );
+  VERIFY( sbuf4.sgetc() == std::stringbuf::traits_type::eof() );
+
+#if __cplusplus >= 201103L
+  static_assert( ! std::is_convertible<std::ios::openmode, std::stringbuf>(),
+                 "stringbuf(ios::openmode) is explicit");
+
+  static_assert( ! std::is_convertible<const std::string&, std::stringbuf>(),
+                 "stringbuf(string, ios::openmode) is explicit");
+#endif
+}
+
+int main()
 {
   test01();
+  test02();
   return 0;
 }
index ce669358c85083c809db0f80ed3551f58b79ed2d..c72ca5c7c84df43fba77031dbee4216f4357d03e 100644 (file)
@@ -15,7 +15,7 @@
 // with this library; see the file COPYING3.  If not see
 // <http://www.gnu.org/licenses/>.
 
-// 27.7.1.1  basic_stringbuf constructors  [lib.stringbuf.cons]
+// C++20 29.8.2.2  basic_stringbuf constructors  [stringbuf.cons]
 
 // { dg-options "-std=gnu++2a" }
 // { dg-do run { target c++2a } }
 #include <testsuite_allocator.h>
 #include <testsuite_hooks.h>
 
+template<typename Alloc, typename C = typename Alloc::value_type>
+  using stringbuf_with_alloc
+    = std::basic_stringbuf<C, std::char_traits<C>, Alloc>;
+
 void
 test01()
 {
+  // Test C++20 constructors taking an allocator but no string.
+
+  static_assert(!std::is_convertible_v<std::allocator<char>, std::stringbuf>,
+      "stringbuf(const allocator<char>&) is explicit");
+
   {
     using alloc_type = __gnu_test::uneq_allocator<char>;
-    using sbuf_t = std::basic_stringbuf<char, std::char_traits<char>,
-                                               alloc_type>;
+    using sbuf_t = stringbuf_with_alloc<alloc_type>;
+
+    static_assert(!std::is_convertible_v<const alloc_type&, sbuf_t>,
+       "basic_stringbuf(const basic_stringbuf::allocator_type&) is explicit");
 
     alloc_type aa;
     sbuf_t sbuf1(aa);
@@ -48,29 +59,64 @@ test01()
   std::stringbuf::allocator_type a;
   {
     std::stringbuf sbuf(std::ios_base::in, a);
+    VERIFY( sbuf.str().empty() );
+
+    std::stringbuf sbuf2 = {std::ios_base::in, a}; // non-explicit ctor
   }
 
   {
     std::stringbuf sbuf(a);
+    VERIFY( sbuf.str().empty() );
   }
 }
 
-auto const cstr = "This is a test";
+auto const cstr = "This is a test string";
 
 void
 test02()
 {
+  // Test C++20 constructor taking an rvalue string
+
+  static_assert(!std::is_convertible_v<std::string, std::stringbuf>,
+      "stringbuf(string&&, ios::openmode) is explicit");
+
   std::string s1(cstr);
-  std::stringbuf sbuf(std::move(s1));
+  std::stringbuf sbuf1(std::move(s1));
   VERIFY( s1.empty() );
+  VERIFY( sbuf1.str() == cstr );
+  VERIFY( sbuf1.sgetc() == cstr[0] );
 
   std::string s2(cstr);
-  VERIFY( sbuf.str() == s2 );
+  std::stringbuf sbuf2(std::move(s2), std::ios_base::in);
+  VERIFY( s2.empty() );
+  VERIFY( sbuf2.str() == cstr );
+  VERIFY( sbuf2.sgetc() == cstr[0] );
+  VERIFY( sbuf2.sputc('X') == std::stringbuf::traits_type::eof() );
+
+  std::string s3(cstr);
+  std::stringbuf sbuf3(std::move(s3), std::ios_base::out);
+  VERIFY( s3.empty() );
+  VERIFY( sbuf3.str() == cstr );
+  VERIFY( sbuf3.sputc('Y') == 'Y' );
+  VERIFY( sbuf3.sgetc() == std::stringbuf::traits_type::eof() );
 }
 
+// A minimal allocator with no default constructor
+template<typename T>
+  struct NoDefaultCons : __gnu_test::SimpleAllocator<T>
+  {
+    using __gnu_test::SimpleAllocator<T>::SimpleAllocator;
+
+    NoDefaultCons() = delete;
+
+    NoDefaultCons(int) { }
+  };
+
 void
 test03()
 {
+  // Test C++20 constructors taking strings using different allocators
+
   using alloc_type = __gnu_test::tracker_allocator<char>;
   using str_type = std::basic_string<char, std::char_traits<char>, alloc_type>;
 
@@ -78,28 +124,92 @@ test03()
   str_type s1(cstr);
 
   {
+    // basic_stringbuf(const basic_string<char, traits_type, SAlloc>&,
+    //                 ios_base::openmode,
+    //                 const allocator_type&)
+
     std::stringbuf::allocator_type a;
-    std::stringbuf sbuf(s1, mode, a);
+    std::stringbuf sbuf = {s1, mode, a}; // ={} checks for non-explicit ctor
     std::string s2(cstr);
     VERIFY( sbuf.str() == s2 );
+
+    std::stringbuf sbuf2 = {std::move(s1), std::ios::in, a};
+    VERIFY( sbuf2.str() == s2 );
+    VERIFY( s1 == cstr ); // did not move from std::move(s1)
+    VERIFY( sbuf2.sgetc() == s1[0] );
+    VERIFY( sbuf2.sputc('X') == std::stringbuf::traits_type::eof() );
+
+    std::stringbuf sbuf3 = {std::move(s1), std::ios::out, a};
+    VERIFY( sbuf3.str() == s2 );
+    VERIFY( s1 == cstr ); // did not move from std::move(s1)
+    VERIFY( sbuf3.sputc('X') == 'X' );
+    VERIFY( sbuf3.sgetc() == std::stringbuf::traits_type::eof() );
   }
 
   {
+    // explicit
+    // basic_stringbuf(const basic_string<char, traits_type, SAlloc>&,
+    //                 ios_base::openmode)
+
     std::stringbuf sbuf(s1, mode);
     std::string s2(cstr);
     VERIFY( sbuf.str() == s2 );
+
+    std::stringbuf sbuf2(std::move(s1), std::ios::in);
+    VERIFY( sbuf2.str() == s2 );
+    VERIFY( s1 == cstr ); // did not move from std::move(s1)
+    VERIFY( sbuf2.sgetc() == s1[0] );
+    VERIFY( sbuf2.sputc('X') == std::stringbuf::traits_type::eof() );
+
+    std::stringbuf sbuf3(std::move(s1), std::ios::out);
+    VERIFY( sbuf3.str() == s2 );
+    VERIFY( s1 == cstr ); // did not move from std::move(s1)
+    VERIFY( sbuf3.sputc('X') == 'X' );
+    VERIFY( sbuf3.sgetc() == std::stringbuf::traits_type::eof() );
   }
 
   {
+    // explicit
+    // basic_stringbuf(const basic_string<char, traits_type, SAlloc>&,
+    //                 ios_base::openmode = ios_base::in|ios_base::out)
+
+    static_assert( ! std::is_convertible_v<str_type, std::stringbuf>,
+       "stringbuf(const basic_string<char, traits_type, SAlloc>&, openmode)"
+       " is explicit");
+
     std::stringbuf sbuf(s1);
     std::string s2(cstr);
     VERIFY( sbuf.str() == s2 );
+
+    std::stringbuf sbuf2(std::move(s1));
+    VERIFY( sbuf2.str() == s2 );
+    VERIFY( s1 == cstr ); // did not move from std::move(s1)
+    VERIFY( sbuf2.sgetc() == s1[0] );
+  }
+
+  {
+    NoDefaultCons<char> a(1);
+    stringbuf_with_alloc<NoDefaultCons<char>> sbuf1(s1, a);
+    VERIFY( sbuf1.str() == cstr );
+    VERIFY( sbuf1.sgetc() == s1[0] );
+
+    stringbuf_with_alloc<NoDefaultCons<char>> sbuf2(s1, std::ios::in, a);
+    VERIFY( sbuf2.str() == cstr );
+    VERIFY( sbuf2.sgetc() == s1[0] );
+    VERIFY( sbuf2.sputc('X') == std::stringbuf::traits_type::eof() );
+
+    stringbuf_with_alloc<NoDefaultCons<char>> sbuf3(s1, std::ios::out, a);
+    VERIFY( sbuf3.str() == cstr );
+    VERIFY( sbuf3.sputc('X') == 'X' );
+    VERIFY( sbuf3.sgetc() == std::stringbuf::traits_type::eof() );
   }
 }
 
 void
 test04()
 {
+  // Test C++20 allocator-extended move constructor
+
   std::stringbuf sbuf1(cstr);
 
   std::stringbuf::allocator_type a;
@@ -117,5 +227,4 @@ main()
   test02();
   test03();
   test04();
-  return 0;
 }
index 7d51744fd8d2fa3eb9e92a497ab34e1db8d03dbf..4e3a2a9631d8f28e935b0258d7569f52cbeebb35 100644 (file)
@@ -17,7 +17,7 @@
 // with this library; see the file COPYING3.  If not see
 // <http://www.gnu.org/licenses/>.
 
-// 27.7.1.1  basic_stringbuf constructors  [lib.stringbuf.cons]
+// C++03 27.7.1.1  basic_stringbuf constructors  [lib.stringbuf.cons]
 
 #include <sstream>
 #include <testsuite_hooks.h>
@@ -30,8 +30,41 @@ void test01()
   VERIFY( sbuf.check_pointers() );
 }
 
-int main() 
+void test02()
+{
+  std::wstringbuf sbuf;
+  VERIFY( sbuf.str().empty() );
+
+  std::wstringbuf sbuf1(std::wios::in);
+  VERIFY( sbuf1.str().empty() );
+
+  const std::wstring str = L"This is my boomstick!";
+
+  std::wstringbuf sbuf2(str);
+  VERIFY( sbuf2.str() == str );
+
+  std::wstringbuf sbuf3(str, std::wios::in);
+  VERIFY( sbuf3.str() == str );
+  VERIFY( sbuf3.sgetc() == str[0] );
+  VERIFY( sbuf3.sputc(L'X') == std::wstringbuf::traits_type::eof() );
+
+  std::wstringbuf sbuf4(str, std::wios::out);
+  VERIFY( sbuf4.str() == str );
+  VERIFY( sbuf4.sputc(L'Y') == L'Y' );
+  VERIFY( sbuf4.sgetc() == std::wstringbuf::traits_type::eof() );
+
+#if __cplusplus >= 201103L
+  static_assert( ! std::is_convertible<std::wios::openmode, std::wstringbuf>(),
+                 "wstringbuf(wios::openmode) is explicit");
+
+  static_assert( ! std::is_convertible<const std::wstring&, std::wstringbuf>(),
+                 "wstringbuf(wstring, wios::openmode) is explicit");
+#endif
+}
+
+int main()
 {
   test01();
+  test02();
   return 0;
 }
index e05acc42165974968625bd8aebf80e184508b4c1..4fbbbf39ad393daaab0fd5cd51d9ae8237380f0e 100644 (file)
@@ -15,7 +15,7 @@
 // with this library; see the file COPYING3.  If not see
 // <http://www.gnu.org/licenses/>.
 
-// 27.7.1.1  basic_stringbuf constructors  [lib.stringbuf.cons]
+// C++20 29.8.2.2  basic_stringbuf constructors  [stringbuf.cons]
 
 // { dg-options "-std=gnu++2a" }
 // { dg-do run { target c++2a } }
 #include <testsuite_allocator.h>
 #include <testsuite_hooks.h>
 
+template<typename Alloc, typename C = typename Alloc::value_type>
+  using stringbuf_with_alloc
+    = std::basic_stringbuf<C, std::char_traits<C>, Alloc>;
+
 void
 test01()
 {
+  // Test C++20 constructors taking an allocator but no string.
+
+  static_assert(!std::is_convertible_v<std::allocator<wchar_t>, std::wstringbuf>,
+      "wstringbuf(const allocator<wchar_t>&) is explicit");
+
+  {
+    using alloc_type = __gnu_test::uneq_allocator<wchar_t>;
+    using sbuf_t = stringbuf_with_alloc<alloc_type>;
+
+    static_assert(!std::is_convertible_v<const alloc_type&, sbuf_t>,
+       "basic_stringbuf(const basic_stringbuf::allocator_type&) is explicit");
+
+    alloc_type aa;
+    sbuf_t sbuf1(aa);
+    VERIFY( aa == sbuf1.get_allocator() );
+
+    alloc_type aaa(42);
+    sbuf_t sbuf2(aaa);
+    VERIFY( aaa == sbuf2.get_allocator() );
+
+    VERIFY( sbuf1.get_allocator() != sbuf2.get_allocator() );
+  }
+
   std::wstringbuf::allocator_type a;
   {
     std::wstringbuf sbuf(std::ios_base::in, a);
+    VERIFY( sbuf.str().empty() );
+
+    std::wstringbuf sbuf2 = {std::ios_base::in, a}; // non-explicit ctor
   }
 
   {
     std::wstringbuf sbuf(a);
+    VERIFY( sbuf.str().empty() );
   }
 }
 
-auto const cstr = L"This is a test";
+auto const cstr = L"This is a test string";
 
 void
 test02()
 {
+  // Test C++20 constructor taking an rvalue string
+
+  static_assert(!std::is_convertible_v<std::wstring, std::wstringbuf>,
+      "wstringbuf(wstring&&, ios::openmode) is explicit");
+
   std::wstring s1(cstr);
-  std::wstringbuf sbuf(std::move(s1));
+  std::wstringbuf sbuf1(std::move(s1));
   VERIFY( s1.empty() );
+  VERIFY( sbuf1.str() == cstr );
+  VERIFY( sbuf1.sgetc() == cstr[0] );
 
   std::wstring s2(cstr);
-  VERIFY( sbuf.str() == s2 );
+  std::wstringbuf sbuf2(std::move(s2), std::ios_base::in);
+  VERIFY( s2.empty() );
+  VERIFY( sbuf2.str() == cstr );
+  VERIFY( sbuf2.sgetc() == cstr[0] );
+  VERIFY( sbuf2.sputc(L'X') == std::wstringbuf::traits_type::eof() );
+
+  std::wstring s3(cstr);
+  std::wstringbuf sbuf3(std::move(s3), std::ios_base::out);
+  VERIFY( s3.empty() );
+  VERIFY( sbuf3.str() == cstr );
+  VERIFY( sbuf3.sputc(L'Y') == L'Y' );
+  VERIFY( sbuf3.sgetc() == std::wstringbuf::traits_type::eof() );
 }
 
+// A minimal allocator with no default constructor
+template<typename T>
+  struct NoDefaultCons : __gnu_test::SimpleAllocator<T>
+  {
+    using __gnu_test::SimpleAllocator<T>::SimpleAllocator;
+
+    NoDefaultCons() = delete;
+
+    NoDefaultCons(int) { }
+  };
+
 void
 test03()
 {
+  // Test C++20 constructors taking strings using different allocators
+
   using alloc_type = __gnu_test::tracker_allocator<wchar_t>;
   using str_type = std::basic_string<wchar_t, std::char_traits<wchar_t>, alloc_type>;
 
@@ -62,28 +124,92 @@ test03()
   str_type s1(cstr);
 
   {
+    // basic_stringbuf(const basic_string<wchar_t, traits_type, SAlloc>&,
+    //                 ios_base::openmode,
+    //                 const allocator_type&)
+
     std::wstringbuf::allocator_type a;
-    std::wstringbuf sbuf(s1, mode, a);
+    std::wstringbuf sbuf = {s1, mode, a}; // ={} checks for non-explicit ctor
     std::wstring s2(cstr);
     VERIFY( sbuf.str() == s2 );
+
+    std::wstringbuf sbuf2 = {std::move(s1), std::ios::in, a};
+    VERIFY( sbuf2.str() == s2 );
+    VERIFY( s1 == cstr ); // did not move from std::move(s1)
+    VERIFY( sbuf2.sgetc() == s1[0] );
+    VERIFY( sbuf2.sputc(L'X') == std::wstringbuf::traits_type::eof() );
+
+    std::wstringbuf sbuf3 = {std::move(s1), std::ios::out, a};
+    VERIFY( sbuf3.str() == s2 );
+    VERIFY( s1 == cstr ); // did not move from std::move(s1)
+    VERIFY( sbuf3.sputc(L'X') == L'X' );
+    VERIFY( sbuf3.sgetc() == std::wstringbuf::traits_type::eof() );
   }
 
   {
+    // explicit
+    // basic_stringbuf(const basic_string<wchar_t, traits_type, SAlloc>&,
+    //                 ios_base::openmode)
+
     std::wstringbuf sbuf(s1, mode);
     std::wstring s2(cstr);
     VERIFY( sbuf.str() == s2 );
+
+    std::wstringbuf sbuf2(std::move(s1), std::ios::in);
+    VERIFY( sbuf2.str() == s2 );
+    VERIFY( s1 == cstr ); // did not move from std::move(s1)
+    VERIFY( sbuf2.sgetc() == s1[0] );
+    VERIFY( sbuf2.sputc(L'X') == std::wstringbuf::traits_type::eof() );
+
+    std::wstringbuf sbuf3(std::move(s1), std::ios::out);
+    VERIFY( sbuf3.str() == s2 );
+    VERIFY( s1 == cstr ); // did not move from std::move(s1)
+    VERIFY( sbuf3.sputc(L'X') == L'X' );
+    VERIFY( sbuf3.sgetc() == std::wstringbuf::traits_type::eof() );
   }
 
   {
+    // explicit
+    // basic_stringbuf(const basic_string<wchar_t, traits_type, SAlloc>&,
+    //                 ios_base::openmode = ios_base::in|ios_base::out)
+
+    static_assert( ! std::is_convertible_v<str_type, std::wstringbuf>,
+       "wstringbuf(const basic_string<wchar_t, traits_type, SAlloc>&,"
+                 " openmode) is explicit");
+
     std::wstringbuf sbuf(s1);
     std::wstring s2(cstr);
     VERIFY( sbuf.str() == s2 );
+
+    std::wstringbuf sbuf2(std::move(s1));
+    VERIFY( sbuf2.str() == s2 );
+    VERIFY( s1 == cstr ); // did not move from std::move(s1)
+    VERIFY( sbuf2.sgetc() == s1[0] );
+  }
+
+  {
+    NoDefaultCons<wchar_t> a(1);
+    stringbuf_with_alloc<NoDefaultCons<wchar_t>> sbuf1(s1, a);
+    VERIFY( sbuf1.str() == cstr );
+    VERIFY( sbuf1.sgetc() == s1[0] );
+
+    stringbuf_with_alloc<NoDefaultCons<wchar_t>> sbuf2(s1, std::ios::in, a);
+    VERIFY( sbuf2.str() == cstr );
+    VERIFY( sbuf2.sgetc() == s1[0] );
+    VERIFY( sbuf2.sputc(L'X') == std::wstringbuf::traits_type::eof() );
+
+    stringbuf_with_alloc<NoDefaultCons<wchar_t>> sbuf3(s1, std::ios::out, a);
+    VERIFY( sbuf3.str() == cstr );
+    VERIFY( sbuf3.sputc(L'X') == L'X' );
+    VERIFY( sbuf3.sgetc() == std::wstringbuf::traits_type::eof() );
   }
 }
 
 void
 test04()
 {
+  // Test C++20 allocator-extended move constructor
+
   std::wstringbuf sbuf1(cstr);
 
   std::wstringbuf::allocator_type a;
@@ -101,5 +227,4 @@ main()
   test02();
   test03();
   test04();
-  return 0;
 }
index 83a3374b3cdbb8ac785f99f200bad2f27976887d..33f2953a6513aab0192ae55dc53c5ea84498e57e 100644 (file)
@@ -33,7 +33,7 @@ test01()
   std::stringstream stm(std::ios_base::in, a);
 }
 
-auto const cstr = "This is a test";
+auto const cstr = "This is a test string";
 
 void
 test02()
@@ -57,29 +57,64 @@ test03()
 
   {
     std::stringstream::allocator_type a;
-    std::stringstream sbuf(s1, mode, a);
+    std::stringstream ss(s1, mode, a);
     std::string s2(cstr);
-    VERIFY( sbuf.str() == s2 );
+    VERIFY( ss.str() == s2 );
   }
 
   {
-    std::stringstream sbuf(s1, mode);
+    std::stringstream ss(s1, mode);
     std::string s2(cstr);
-    VERIFY( sbuf.str() == s2 );
+    VERIFY( ss.str() == s2 );
   }
 
   {
-    std::stringstream sbuf(s1);
+    std::stringstream ss(s1);
     std::string s2(cstr);
-    VERIFY( sbuf.str() == s2 );
+    VERIFY( ss.str() == s2 );
   }
 }
 
+// A minimal allocator with no default constructor
+template<typename T>
+  struct NoDefaultCons : __gnu_test::SimpleAllocator<T>
+  {
+    using __gnu_test::SimpleAllocator<T>::SimpleAllocator;
+
+    NoDefaultCons() = delete;
+
+    NoDefaultCons(int) { }
+  };
+
+void
+test04()
+{
+  using sstream = std::basic_stringstream<char, std::char_traits<char>,
+                                         NoDefaultCons<char>>;
+
+  NoDefaultCons<char> a(1);
+  const std::string str(cstr);
+
+  sstream ss1(str, a);
+  VERIFY( ss1.str() == cstr );
+  VERIFY( ss1.get() == cstr[0] );
+
+  sstream ss2(str, std::ios::in, a);
+  VERIFY( ss2.str() == cstr );
+  VERIFY( ss2.get() == cstr[0] );
+  VERIFY( !bool(ss2 << 1) );
+
+  sstream ss3(std::string(str), std::ios::out, a);
+  VERIFY( ss3.str() == cstr );
+  VERIFY( bool(ss3 << 1) );
+  VERIFY( ss3.get() == std::wstringbuf::traits_type::eof() );
+}
+
 int
 main()
 {
   test01();
   test02();
   test03();
-  return 0;
+  test04();
 }