From 432258be4f2cf4f0970f106db319e3dbab4ab13d Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Thu, 5 Nov 2020 12:16:13 +0000 Subject: [PATCH] libstdc++: Fix new 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&, 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&, openmode, const A&)): Pass string to _M_streambuf instead of constructing a temporary with the wrong allocator. (basic_ostringstream(const basic_string&, openmode, const A&)): Likewise. (basic_stringstream(const basic_string&, 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 | 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 ++++- .../27_io/basic_stringbuf/cons/char/1.cc | 37 ++++- .../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; - + 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 - 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 - 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 explicit @@ -258,9 +257,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 } #if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI - __sv_type + basic_string_view view() const noexcept { + using __sv_type = basic_string_view; + 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 @@ -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 @@ -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 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::basic_stringbuf(basic_stringbuf&&, const allocator_type&); template basic_stringbuf::allocator_type basic_stringbuf::get_allocator() const noexcept; -template basic_stringbuf::__sv_type +template string_view basic_stringbuf::view() const noexcept; template basic_istringstream::basic_istringstream(ios_base::openmode, @@ -68,8 +68,6 @@ template string_view basic_stringstream::view() const noexcept; #ifdef _GLIBCXX_USE_WCHAR_T -using wsv_type = basic_string_view; - template basic_stringbuf::basic_stringbuf(const allocator_type&); template basic_stringbuf::basic_stringbuf(ios_base::openmode, const allocator_type&); @@ -80,7 +78,7 @@ template basic_stringbuf::basic_stringbuf(basic_stringbuf&&, template basic_stringbuf::allocator_type basic_stringbuf::get_allocator() const noexcept; -template basic_istringstream::__stringbuf_type::__sv_type +template wstring_view basic_stringbuf::view() const noexcept; template basic_istringstream::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 + struct NoDefaultCons : __gnu_test::SimpleAllocator + { + using __gnu_test::SimpleAllocator::SimpleAllocator; + + NoDefaultCons() = delete; + + NoDefaultCons(int) { } + }; + +void +test04() +{ + using sstream = std::basic_istringstream, + NoDefaultCons>; + + NoDefaultCons 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 + struct NoDefaultCons : __gnu_test::SimpleAllocator + { + using __gnu_test::SimpleAllocator::SimpleAllocator; + + NoDefaultCons() = delete; + + NoDefaultCons(int) { } + }; + +void +test04() +{ + using sstream = std::basic_ostringstream, + NoDefaultCons>; + + NoDefaultCons 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 // . -// 27.7.1.1 basic_stringbuf constructors [lib.stringbuf.cons] +// C++03 27.7.1.1 basic_stringbuf constructors [lib.stringbuf.cons] #include #include @@ -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(), + "stringbuf(ios::openmode) is explicit"); + + static_assert( ! std::is_convertible(), + "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 // . -// 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 #include +template + using stringbuf_with_alloc + = std::basic_stringbuf, Alloc>; + void test01() { + // Test C++20 constructors taking an allocator but no string. + + static_assert(!std::is_convertible_v, std::stringbuf>, + "stringbuf(const allocator&) is explicit"); + { using alloc_type = __gnu_test::uneq_allocator; - using sbuf_t = std::basic_stringbuf, - alloc_type>; + using sbuf_t = stringbuf_with_alloc; + + static_assert(!std::is_convertible_v, + "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, + "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 + struct NoDefaultCons : __gnu_test::SimpleAllocator + { + using __gnu_test::SimpleAllocator::SimpleAllocator; + + NoDefaultCons() = delete; + + NoDefaultCons(int) { } + }; + void test03() { + // Test C++20 constructors taking strings using different allocators + using alloc_type = __gnu_test::tracker_allocator; using str_type = std::basic_string, alloc_type>; @@ -78,28 +124,92 @@ test03() str_type s1(cstr); { + // basic_stringbuf(const basic_string&, + // 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&, + // 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&, + // ios_base::openmode = ios_base::in|ios_base::out) + + static_assert( ! std::is_convertible_v, + "stringbuf(const basic_string&, 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 a(1); + stringbuf_with_alloc> sbuf1(s1, a); + VERIFY( sbuf1.str() == cstr ); + VERIFY( sbuf1.sgetc() == s1[0] ); + + stringbuf_with_alloc> 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> 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 // . -// 27.7.1.1 basic_stringbuf constructors [lib.stringbuf.cons] +// C++03 27.7.1.1 basic_stringbuf constructors [lib.stringbuf.cons] #include #include @@ -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(), + "wstringbuf(wios::openmode) is explicit"); + + static_assert( ! std::is_convertible(), + "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 // . -// 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 #include +template + using stringbuf_with_alloc + = std::basic_stringbuf, Alloc>; + void test01() { + // Test C++20 constructors taking an allocator but no string. + + static_assert(!std::is_convertible_v, std::wstringbuf>, + "wstringbuf(const allocator&) is explicit"); + + { + using alloc_type = __gnu_test::uneq_allocator; + using sbuf_t = stringbuf_with_alloc; + + static_assert(!std::is_convertible_v, + "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, + "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 + struct NoDefaultCons : __gnu_test::SimpleAllocator + { + using __gnu_test::SimpleAllocator::SimpleAllocator; + + NoDefaultCons() = delete; + + NoDefaultCons(int) { } + }; + void test03() { + // Test C++20 constructors taking strings using different allocators + using alloc_type = __gnu_test::tracker_allocator; using str_type = std::basic_string, alloc_type>; @@ -62,28 +124,92 @@ test03() str_type s1(cstr); { + // basic_stringbuf(const basic_string&, + // 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&, + // 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&, + // ios_base::openmode = ios_base::in|ios_base::out) + + static_assert( ! std::is_convertible_v, + "wstringbuf(const basic_string&," + " 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 a(1); + stringbuf_with_alloc> sbuf1(s1, a); + VERIFY( sbuf1.str() == cstr ); + VERIFY( sbuf1.sgetc() == s1[0] ); + + stringbuf_with_alloc> 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> 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 + struct NoDefaultCons : __gnu_test::SimpleAllocator + { + using __gnu_test::SimpleAllocator::SimpleAllocator; + + NoDefaultCons() = delete; + + NoDefaultCons(int) { } + }; + +void +test04() +{ + using sstream = std::basic_stringstream, + NoDefaultCons>; + + NoDefaultCons 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(); } -- 2.30.2