public inbox for libstdc++-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r11-4738] libstdc++: Fix new <sstream> constructors
@ 2020-11-05 13:31 Jonathan Wakely
  0 siblings, 0 replies; only message in thread
From: Jonathan Wakely @ 2020-11-05 13:31 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:432258be4f2cf4f0970f106db319e3dbab4ab13d

commit r11-4738-g432258be4f2cf4f0970f106db319e3dbab4ab13d
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Nov 5 12:16:13 2020 +0000

    libstdc++: Fix new <sstream> constructors
    
    - 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.

Diff:
---
 libstdc++-v3/include/std/sstream                   |  38 +++---
 libstdc++-v3/src/c++20/sstream-inst.cc             |   6 +-
 .../27_io/basic_istringstream/cons/char/1.cc       |  37 +++++-
 .../27_io/basic_ostringstream/cons/char/1.cc       |  38 +++++-
 .../testsuite/27_io/basic_stringbuf/cons/char/1.cc |  37 +++++-
 .../testsuite/27_io/basic_stringbuf/cons/char/2.cc | 125 +++++++++++++++++--
 .../27_io/basic_stringbuf/cons/wchar_t/1.cc        |  37 +++++-
 .../27_io/basic_stringbuf/cons/wchar_t/2.cc        | 137 ++++++++++++++++++++-
 .../27_io/basic_stringstream/cons/char/1.cc        |  51 ++++++--
 9 files changed, 451 insertions(+), 55 deletions(-)

diff --git a/libstdc++-v3/include/std/sstream b/libstdc++-v3/include/std/sstream
index 276badfd965..437e2ba2a5f 100644
--- a/libstdc++-v3/include/std/sstream
+++ b/libstdc++-v3/include/std/sstream
@@ -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>
diff --git a/libstdc++-v3/src/c++20/sstream-inst.cc b/libstdc++-v3/src/c++20/sstream-inst.cc
index 8c6840115c5..ada3eabac1f 100644
--- a/libstdc++-v3/src/c++20/sstream-inst.cc
+++ b/libstdc++-v3/src/c++20/sstream-inst.cc
@@ -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,
diff --git a/libstdc++-v3/testsuite/27_io/basic_istringstream/cons/char/1.cc b/libstdc++-v3/testsuite/27_io/basic_istringstream/cons/char/1.cc
index 496aa6963e0..6584b88193d 100644
--- a/libstdc++-v3/testsuite/27_io/basic_istringstream/cons/char/1.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_istringstream/cons/char/1.cc
@@ -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();
 }
diff --git a/libstdc++-v3/testsuite/27_io/basic_ostringstream/cons/char/1.cc b/libstdc++-v3/testsuite/27_io/basic_ostringstream/cons/char/1.cc
index cba69f80931..885949caf00 100644
--- a/libstdc++-v3/testsuite/27_io/basic_ostringstream/cons/char/1.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_ostringstream/cons/char/1.cc
@@ -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();
 }
diff --git a/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/char/1.cc b/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/char/1.cc
index 0e9649798f8..bd17e6ddc9e 100644
--- a/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/char/1.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/char/1.cc
@@ -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;
 }
diff --git a/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/char/2.cc b/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/char/2.cc
index ce669358c85..c72ca5c7c84 100644
--- a/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/char/2.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/char/2.cc
@@ -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 } }
@@ -26,13 +26,24 @@
 #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;
 }
diff --git a/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/wchar_t/1.cc b/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/wchar_t/1.cc
index 7d51744fd8d..4e3a2a9631d 100644
--- a/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/wchar_t/1.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/wchar_t/1.cc
@@ -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;
 }
diff --git a/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/wchar_t/2.cc b/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/wchar_t/2.cc
index e05acc42165..4fbbbf39ad3 100644
--- a/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/wchar_t/2.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/wchar_t/2.cc
@@ -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 } }
@@ -26,35 +26,97 @@
 #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;
 }
diff --git a/libstdc++-v3/testsuite/27_io/basic_stringstream/cons/char/1.cc b/libstdc++-v3/testsuite/27_io/basic_stringstream/cons/char/1.cc
index 83a3374b3cd..33f2953a651 100644
--- a/libstdc++-v3/testsuite/27_io/basic_stringstream/cons/char/1.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_stringstream/cons/char/1.cc
@@ -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();
 }


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2020-11-05 13:31 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-05 13:31 [gcc r11-4738] libstdc++: Fix new <sstream> constructors 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).