On 29/05/25 09:50 -0400, Nathan Myers wrote:
Change in V3:
* Comment that p2495 specifies a drive-by constraint omitted as redundant
* Adjust whitespace to fit in 80 columns
Change in V2:
* apply all review comments
* remove redundant drive-by "requires" on ctor from string allocator arg
* check allocators are plumbed through
-- >8 --
Implement PR libstdc++/119741 (P2495R3)
Add constructors to stringbuf, stringstream, istringstream,
and ostringstream, and a matching overload of str(sv) in each,
that take anything convertible to a string_view in places
where the existing functions take a string.
libstdc++-v3/ChangeLog:
PR libstdc++/119741
* include/std/sstream: full implementation, really just
decls, requires clause and plumbing.
* include/bits/version.def, include/bits/version.h:
new preprocessor symbol
__cpp_lib_sstream_from_string_view.
* testsuite/27_io/basic_stringbuf/cons/char/3.cc: New tests.
* testsuite/27_io/basic_istringstream/cons/char/2.cc: New tests.
* testsuite/27_io/basic_ostringstream/cons/char/4.cc: New tests.
* testsuite/27_io/basic_stringstream/cons/char/2.cc: New tests.
Historically we just named most tests as 1.cc, 2.cc, 3.cc but it's not
very helpful when you see "FAIL: .../1.cc" in the test logs, so since
these new tests are for specific new constructors, I think it would
make more sense for them to all be named string_view.cc
That makes it very clear that they're testing construction from
string_view.
Please add some wchar_t tests too, i.e. cons/wchar_t/string_view.cc
That ensures that the impl doesn't accidentally use char_traits<char>
where it should be char_traits<_CharT> or anything like that. If the
tests only use the char specializations then we wouldn't notice. The
wchar_t tests can be copies of the char ones, with char -> wchar_t
substituted
Or if you want to avoid copy&pasting the whole test, you could use C
instead of char and do this in the char/*.cc versions:
#ifndef C
#define C char
#endif
and then in the wchar_t/*.cc versions do:
#define C wchar_t
#include "../char/string_view.cc"
So that you reuse the char ones.
---
libstdc++-v3/include/bits/version.def | 11 +-
libstdc++-v3/include/bits/version.h | 10 +
libstdc++-v3/include/std/sstream | 200 ++++++++++++++++--
.../27_io/basic_istringstream/cons/char/2.cc | 187 ++++++++++++++++
.../27_io/basic_ostringstream/cons/char/4.cc | 186 ++++++++++++++++
.../27_io/basic_stringbuf/cons/char/3.cc | 196 +++++++++++++++++
.../27_io/basic_stringstream/cons/char/2.cc | 196 +++++++++++++++++
7 files changed, 967 insertions(+), 19 deletions(-)
create mode 100644
libstdc++-v3/testsuite/27_io/basic_istringstream/cons/char/2.cc
create mode 100644
libstdc++-v3/testsuite/27_io/basic_ostringstream/cons/char/4.cc
create mode 100644 libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/char/3.cc
create mode 100644
libstdc++-v3/testsuite/27_io/basic_stringstream/cons/char/2.cc
diff --git a/libstdc++-v3/include/bits/version.def
b/libstdc++-v3/include/bits/version.def
index 282667eabda..8172bcd4e26 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -649,7 +649,7 @@ ftms = {
};
values = {
v = 1;
- /* For when there's no gthread. */
+ // For when there is no gthread.
cxxmin = 17;
hosted = yes;
gthread = no;
@@ -1945,6 +1945,15 @@ ftms = {
};
};
+ftms = {
+ name = sstream_from_string_view;
+ values = {
+ v = 202302;
The correct value for this macro is 202306, see [version.syn] in the
working draft, or SD-6:
https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations#__cpp_lib_sstream_from_string_view
+ cxxmin = 26;
+ hosted = yes;
+ };
+};
+
// Standard test specifications.
stds[97] = ">= 199711L";
stds[03] = ">= 199711L";
diff --git a/libstdc++-v3/include/bits/version.h
b/libstdc++-v3/include/bits/version.h
index bb7c0479c72..b4b487fba92 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -2174,4 +2174,14 @@
#endif /* !defined(__cpp_lib_modules) && defined(__glibcxx_want_modules) */
#undef __glibcxx_want_modules
+#if !defined(__cpp_lib_sstream_from_string_view)
+# if (__cplusplus > 202302L) && _GLIBCXX_HOSTED
+# define __glibcxx_sstream_from_string_view 202302L
+# if defined(__glibcxx_want_all) ||
defined(__glibcxx_want_sstream_from_string_view)
+# define __cpp_lib_sstream_from_string_view 202302L
+# endif
+# endif
+#endif /* !defined(__cpp_lib_sstream_from_string_view) &&
defined(__glibcxx_want_sstream_from_string_view) */
+#undef __glibcxx_want_sstream_from_string_view
+
#undef __glibcxx_want_all
diff --git a/libstdc++-v3/include/std/sstream b/libstdc++-v3/include/std/sstream
index ad0c16a91e8..528756ed631 100644
--- a/libstdc++-v3/include/std/sstream
+++ b/libstdc++-v3/include/std/sstream
@@ -38,9 +38,14 @@
#endif
#include <bits/requires_hosted.h> // iostream
+#include <bits/version.h>
As Tomasz said, you need to define
__glibcxx_want_sstream_from_string_view before including
bits/version.h because otherwise it won't define the
__cpp_lib_sstream_from_string_view macro that should be defined by
this file.
Also please do that (define the "want" macro and include
bits/version.h) after the other includes, so that
__cpp_lib_sstream_from_string_view gets defined last, after including
other headers, so that those other headers don't see it defined).
#include <istream>
#include <ostream>
+#ifdef __cpp_lib_sstream_from_string_view
+# include <type_traits> // is_convertible_v
We don't need to include <type_traits> here, it's already included by
both <istream> and <ostream>, and <bits/alloc_traits.h>. Apart from
<bits/c++config.h>, the headers that you can pretty much guarantee are
already included everywhere are <bits/move.h> and <type_traits>.
+#endif
+
#include <bits/alloc_traits.h> // allocator_traits, __allocator_like
#if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI
@@ -52,8 +57,6 @@
# define _GLIBCXX_SSTREAM_ALWAYS_INLINE [[__gnu__::__always_inline__]]
#endif
-
-
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
@@ -159,6 +162,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
{ __rhs._M_sync(const_cast<char_type*>(__rhs._M_string.data()), 0, 0); }
#if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI
+ // P0408 Efficient access to basic_stringbuf buffer
explicit
basic_stringbuf(const allocator_type& __a)
: basic_stringbuf(ios_base::in | std::ios_base::out, __a)
@@ -197,7 +201,36 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
| ios_base::out)
: basic_stringbuf(__s, __mode, allocator_type{})
{ }
+#endif
+
+#ifdef __cpp_lib_sstream_from_string_view
+ template<typename _Tp>
+ explicit
+ basic_stringbuf(const _Tp& __t,
+ ios_base::openmode __mode = ios_base::in | ios_base::out)
+ requires (is_convertible_v<const _Tp&,
+ basic_string_view<_CharT, _Traits>>)
+ : basic_stringbuf(__t, __mode, allocator_type{})
+ { }
+
+ template<typename _Tp>
+ basic_stringbuf(const _Tp& __t, const allocator_type& __a)
+ requires (is_convertible_v<const _Tp&,
+ basic_string_view<_CharT, _Traits>>)
+ : basic_stringbuf(__t, ios_base::in | ios_base::out, __a)
+ { }
+ template<typename _Tp>
+ basic_stringbuf(const _Tp& __t, ios_base::openmode __mode,
+ const allocator_type& __a)
+ requires (is_convertible_v<const _Tp&,
+ basic_string_view<_CharT, _Traits>>)
+ : _M_string(__t, __a)
+ { _M_stringbuf_init(__mode); }
+#endif // C++26
+
+#if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI
+ // P0408 Efficient access to basic_stringbuf buffer
basic_stringbuf(basic_stringbuf&& __rhs, const allocator_type& __a)
: basic_stringbuf(std::move(__rhs), __a, __xfer_bufptrs(__rhs, this))
{ __rhs._M_sync(const_cast<char_type*>(__rhs._M_string.data()), 0, 0); }
@@ -262,6 +295,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
#if __cplusplus > 201703L
#if _GLIBCXX_USE_CXX11_ABI
#if __cpp_concepts
+ // P0407 Allocator-aware basic_streambuf
template<__allocator_like _SAlloc>
_GLIBCXX_NODISCARD
basic_string<_CharT, _Traits, _SAlloc>
@@ -317,6 +351,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
#if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI
#if __cpp_concepts
+ // P0407 Allocator-aware basic_streambuf
template<__allocator_like _SAlloc>
requires (!is_same_v<_SAlloc, _Alloc>)
void
@@ -335,6 +370,19 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
}
#endif
+#ifdef __cpp_lib_sstream_from_string_view
+ template <typename _Tp>
+ void
+ str(const _Tp& __t)
+ requires (is_convertible_v<const _Tp&,
+ basic_string_view<_CharT, _Traits>>)
+ {
+ basic_string_view<_CharT, _Traits> __sv{__t};
+ _M_string = __sv;
+ _M_stringbuf_init(_M_mode);
+ }
+#endif // C++26
+
protected:
// Common initialization code goes here.
void
@@ -521,6 +569,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
{ }
#if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI
+ // P0408 Efficient access to basic_stringbuf buffer
+
// The move constructor initializes an __xfer_bufptrs temporary then
// delegates to this constructor to performs moves during its lifetime.
basic_stringbuf(basic_stringbuf&& __rhs, const allocator_type& __a,
@@ -584,7 +634,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
*/
basic_istringstream()
: __istream_type(), _M_stringbuf(ios_base::in)
- { this->init(&_M_stringbuf); }
+ { this->init(std::__addressof(_M_stringbuf)); }
/**
* @brief Starts with an empty string buffer.
@@ -601,7 +651,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
explicit
basic_istringstream(ios_base::openmode __mode)
: __istream_type(), _M_stringbuf(__mode | ios_base::in)
- { this->init(&_M_stringbuf); }
+ { this->init(std::__addressof(_M_stringbuf)); }
/**
* @brief Starts with an existing string buffer.
@@ -620,7 +670,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
basic_istringstream(const __string_type& __str,
ios_base::openmode __mode = ios_base::in)
: __istream_type(), _M_stringbuf(__str, __mode | ios_base::in)
- { this->init(&_M_stringbuf); }
+ { this->init(std::__addressof(_M_stringbuf)); }
/**
* @brief The destructor does nothing.
@@ -637,9 +687,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
basic_istringstream(basic_istringstream&& __rhs)
: __istream_type(std::move(__rhs)),
_M_stringbuf(std::move(__rhs._M_stringbuf))
- { __istream_type::set_rdbuf(&_M_stringbuf); }
+ { __istream_type::set_rdbuf(std::__addressof(_M_stringbuf)); }
#if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI
+ // P0408 Efficient access to basic_stringbuf buffer
basic_istringstream(ios_base::openmode __mode, const allocator_type& __a)
: __istream_type(), _M_stringbuf(__mode | ios_base::in, __a)
{ this->init(std::__addressof(_M_stringbuf)); }
@@ -663,6 +714,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
: __istream_type(), _M_stringbuf(__str, __mode | ios_base::in, __a)
{ this->init(std::__addressof(_M_stringbuf)); }
+ // Implementation note: P2495R3 specifies a "drive-by" addition of a
+ // `requires` clause guarding against _Alloc matching _SAlloc, which
+ // we have determined would be redundant because overloading already
+ // prefers the other constructor.
I'd prefer this to be in the git commit message not a comment.
template<typename _SAlloc>
explicit
basic_istringstream(const basic_string<_CharT, _Traits, _SAlloc>& __str,
@@ -671,6 +726,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
{ }
#endif // C++20
+#ifdef __cpp_lib_sstream_from_string_view
+ template <typename _Tp>
+ explicit basic_istringstream(const _Tp& __t,
Please add a line break after 'explicit' so that the name of the
function (or the class name, in the case of constructors and
destructors) can be found consistently in the same column.
+ ios_base::openmode __mode = ios_base::in)
+ requires (is_convertible_v<const _Tp&,
+ basic_string_view<_CharT, _Traits>>)
+ : basic_istringstream(__t, __mode, allocator_type{})
+ { }
+
+ template <typename _Tp>
+ basic_istringstream(const _Tp& __t, const allocator_type& __a)
+ requires (is_convertible_v<const _Tp&,
+ basic_string_view<_CharT, _Traits>>)
+ : basic_istringstream(__t, ios_base::in, __a)
+ { }
+
+ template <typename _Tp>
+ basic_istringstream(const _Tp& __t, ios_base::openmode __mode,
+ const allocator_type& __a)
+ requires (is_convertible_v<const _Tp&,
+ basic_string_view<_CharT, _Traits>>)
+ : __istream_type(), _M_stringbuf(__t, __mode | ios_base::in, __a)
+ { this->init(std::__addressof(_M_stringbuf)); }
+#endif // C++26
+
// 27.8.3.2 Assign and swap:
basic_istringstream&
@@ -702,7 +782,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
_GLIBCXX_NODISCARD
__stringbuf_type*
rdbuf() const
- { return const_cast<__stringbuf_type*>(&_M_stringbuf); }
+ { return const_cast<__stringbuf_type*>(std::__addressof(_M_stringbuf)); }
/**
* @brief Copying out the string buffer.
@@ -716,6 +796,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
#if __cplusplus > 201703L
#if _GLIBCXX_USE_CXX11_ABI
#if __cpp_concepts
+ // P0407 Allocator-aware basic_streambuf
template<__allocator_like _SAlloc>
_GLIBCXX_NODISCARD
basic_string<_CharT, _Traits, _SAlloc>
@@ -747,6 +828,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
#if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI
#if __cpp_concepts
+ // P0407 Allocator-aware basic_streambuf
template<__allocator_like _SAlloc>
requires (!is_same_v<_SAlloc, _Alloc>)
void
@@ -758,6 +840,15 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
str(__string_type&& __s)
{ _M_stringbuf.str(std::move(__s)); }
#endif
+
+#ifdef __cpp_lib_sstream_from_string_view
+ template<typename _Tp>
+ void
+ str(const _Tp& __t)
+ requires (is_convertible_v<const _Tp&,
+ basic_string_view<_CharT, _Traits>>)
+ { _M_stringbuf.str(__t); }
+#endif // C++26
};
@@ -812,7 +903,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
*/
basic_ostringstream()
: __ostream_type(), _M_stringbuf(ios_base::out)
- { this->init(&_M_stringbuf); }
+ { this->init(std::__addressof(_M_stringbuf)); }
/**
* @brief Starts with an empty string buffer.
@@ -829,7 +920,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
explicit
basic_ostringstream(ios_base::openmode __mode)
: __ostream_type(), _M_stringbuf(__mode | ios_base::out)
- { this->init(&_M_stringbuf); }
+ { this->init(std::__addressof(_M_stringbuf)); }
/**
* @brief Starts with an existing string buffer.
@@ -848,7 +939,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
basic_ostringstream(const __string_type& __str,
ios_base::openmode __mode = ios_base::out)
: __ostream_type(), _M_stringbuf(__str, __mode | ios_base::out)
- { this->init(&_M_stringbuf); }
+ { this->init(std::__addressof(_M_stringbuf)); }
/**
* @brief The destructor does nothing.
@@ -865,9 +956,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
basic_ostringstream(basic_ostringstream&& __rhs)
: __ostream_type(std::move(__rhs)),
_M_stringbuf(std::move(__rhs._M_stringbuf))
- { __ostream_type::set_rdbuf(&_M_stringbuf); }
+ { __ostream_type::set_rdbuf(std::__addressof(_M_stringbuf)); }
#if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI
+ // P0408 Efficient access to basic_stringbuf buffer
basic_ostringstream(ios_base::openmode __mode, const allocator_type& __a)
: __ostream_type(), _M_stringbuf(__mode | ios_base::out, __a)
{ this->init(std::__addressof(_M_stringbuf)); }
@@ -899,6 +991,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
{ }
#endif // C++20
+#ifdef __cpp_lib_sstream_from_string_view
+ template <typename _Tp>
+ explicit basic_ostringstream(
Line break after explicit please.
+ const _Tp& __t, ios_base::openmode __mode = ios_base::out)
+ requires (is_convertible_v<const _Tp&,
+ basic_string_view<_CharT, _Traits>>)
+ : basic_ostringstream(__t, __mode, allocator_type{})
+ { }
+
+ template <typename _Tp>
+ basic_ostringstream(const _Tp& __t, const allocator_type& __a)
+ requires (is_convertible_v<const _Tp&,
+ basic_string_view<_CharT, _Traits>>)
+ : basic_ostringstream(__t, ios_base::out, __a)
+ { }
+
+ template <typename _Tp>
+ basic_ostringstream(const _Tp& __t, ios_base::openmode __mode,
+ const allocator_type& __a)
+ requires (is_convertible_v<const _Tp&,
+ basic_string_view<_CharT, _Traits>>)
+ : __ostream_type(), _M_stringbuf(__t, __mode | ios_base::out, __a)
+ { this->init(std::__addressof(_M_stringbuf)); }
+#endif // C++26
+
// 27.8.3.2 Assign and swap:
basic_ostringstream&
@@ -930,7 +1047,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
_GLIBCXX_NODISCARD
__stringbuf_type*
rdbuf() const
- { return const_cast<__stringbuf_type*>(&_M_stringbuf); }
+ { return const_cast<__stringbuf_type*>(std::__addressof(_M_stringbuf)); }
/**
* @brief Copying out the string buffer.
@@ -944,6 +1061,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
#if __cplusplus > 201703L
#if _GLIBCXX_USE_CXX11_ABI
#if __cpp_concepts
+ // P0407 Allocator-aware basic_streambuf
template<__allocator_like _SAlloc>
_GLIBCXX_NODISCARD
basic_string<_CharT, _Traits, _SAlloc>
@@ -975,6 +1093,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
#if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI
#if __cpp_concepts
+ // P0407 Allocator-aware basic_streambuf
template<__allocator_like _SAlloc>
requires (!is_same_v<_SAlloc, _Alloc>)
void
@@ -986,6 +1105,15 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
str(__string_type&& __s)
{ _M_stringbuf.str(std::move(__s)); }
#endif
+
+#ifdef __cpp_lib_sstream_from_string_view
+ template<typename _Tp>
+ void
+ str(const _Tp& __t)
+ requires (is_convertible_v<const _Tp&,
+ basic_string_view<_CharT, _Traits>>)
+ { _M_stringbuf.str(__t); }
+#endif // C++26
};
@@ -1040,7 +1168,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
*/
basic_stringstream()
: __iostream_type(), _M_stringbuf(ios_base::out | ios_base::in)
- { this->init(&_M_stringbuf); }
+ { this->init(std::__addressof(_M_stringbuf)); }
/**
* @brief Starts with an empty string buffer.
@@ -1055,7 +1183,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
explicit
basic_stringstream(ios_base::openmode __m)
: __iostream_type(), _M_stringbuf(__m)
- { this->init(&_M_stringbuf); }
+ { this->init(std::__addressof(_M_stringbuf)); }
/**
* @brief Starts with an existing string buffer.
@@ -1072,7 +1200,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
basic_stringstream(const __string_type& __str,
ios_base::openmode __m = ios_base::out | ios_base::in)
: __iostream_type(), _M_stringbuf(__str, __m)
- { this->init(&_M_stringbuf); }
+ { this->init(std::__addressof(_M_stringbuf)); }
/**
* @brief The destructor does nothing.
@@ -1089,12 +1217,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
basic_stringstream(basic_stringstream&& __rhs)
: __iostream_type(std::move(__rhs)),
_M_stringbuf(std::move(__rhs._M_stringbuf))
- { __iostream_type::set_rdbuf(&_M_stringbuf); }
+ { __iostream_type::set_rdbuf(std::__addressof(_M_stringbuf)); }
#if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI
+ // P0408 Efficient access to basic_stringbuf buffer
basic_stringstream(ios_base::openmode __mode, const allocator_type& __a)
: __iostream_type(), _M_stringbuf(__mode, __a)
- { this->init(&_M_stringbuf); }
+ { this->init(std::__addressof(_M_stringbuf)); }
explicit
basic_stringstream(__string_type&& __str,
@@ -1125,6 +1254,30 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
{ }
#endif // C++20
+#ifdef __cpp_lib_sstream_from_string_view
+ template <typename _Tp>
+ explicit basic_stringstream(const _Tp& __t,
Line break after explicit please.
+ ios_base::openmode __mode = ios_base::in | ios_base::out)
+ requires (is_convertible_v<const _Tp&,
+ basic_string_view<_CharT, _Traits>>)
+ : basic_stringstream(__t, __mode, allocator_type{})
+ { }
+
+ template <typename _Tp>
+ basic_stringstream(const _Tp& __t, const allocator_type& __a)
+ requires (is_convertible_v<const _Tp&,
+ basic_string_view<_CharT, _Traits>>)
+ : basic_stringstream(__t, ios_base::in | ios_base::out, __a)
+ { }
+
+ template <typename _Tp>
+ basic_stringstream(const _Tp& __t, ios_base::openmode __mode,
+ const allocator_type& __a)
+ requires (is_convertible_v<const _Tp&, basic_string_view<_CharT,
_Traits>>)
+ : __iostream_type(), _M_stringbuf(__t, __mode, __a)
+ { this->init(std::__addressof(_M_stringbuf)); }
+#endif // C++26
+
// 27.8.3.2 Assign and swap:
basic_stringstream&
@@ -1156,7 +1309,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
_GLIBCXX_NODISCARD
__stringbuf_type*
rdbuf() const
- { return const_cast<__stringbuf_type*>(&_M_stringbuf); }
+ { return const_cast<__stringbuf_type*>(std::__addressof(_M_stringbuf)); }
/**
* @brief Copying out the string buffer.
@@ -1170,6 +1323,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
#if __cplusplus > 201703L
#if _GLIBCXX_USE_CXX11_ABI
#if __cpp_concepts
+ // P0407 Allocator-aware basic_streambuf
template<__allocator_like _SAlloc>
_GLIBCXX_NODISCARD
basic_string<_CharT, _Traits, _SAlloc>
@@ -1201,6 +1355,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
#if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI
#if __cpp_concepts
+ // P0407 Allocator-aware basic_streambuf
template<__allocator_like _SAlloc>
requires (!is_same_v<_SAlloc, _Alloc>)
void
@@ -1212,6 +1367,15 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
str(__string_type&& __s)
{ _M_stringbuf.str(std::move(__s)); }
#endif
+
+#ifdef __cpp_lib_sstream_from_string_view
+ template<typename _Tp>
+ void
+ str(const _Tp& __t)
+ requires (is_convertible_v<const _Tp&,
+ basic_string_view<_CharT, _Traits>>)
+ { _M_stringbuf.str(__t); }
+#endif // C++26
};
#if __cplusplus >= 201103L
diff --git a/libstdc++-v3/testsuite/27_io/basic_istringstream/cons/char/2.cc
b/libstdc++-v3/testsuite/27_io/basic_istringstream/cons/char/2.cc
new file mode 100644
index 00000000000..811afafbf90
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_istringstream/cons/char/2.cc
@@ -0,0 +1,187 @@
+// C++26 [istringstream.general]
+
+// { dg-do run { target c++26 } }
+// { dg-require-effective-target cxx11_abi }
The new features do not depend on the cxx11 abi (they're also defined
when using the COW std::string) so this dg-require-effective-target
shouldn't be present in any of the new tests.
+
+#include <sstream>
+#include <string>
+#include <string_view>
+#include <testsuite_allocator.h>
+#include <testsuite_hooks.h>
+
+// Check C++26 P2495 istringstream ctors and members str(s) that accept a
+// string_view, or anything convertible to a string_view, in place of a
+// string object. Mostly just verify plumbing.
+
+struct convertible_to_string_view {
+ std::string s;
+ operator std::string_view() const { return s; }
+};
+
+const std::string str("This is a test string");
+convertible_to_string_view cstr{str}; // a copy
+const convertible_to_string_view ccstr{str}; // another copy
+
+template <typename istringstream = std::basic_istringstream<char>>
+void
+test01()
+{
+ // Test C++26 constructor and str(s) taking a generalized string_view
+
+ static_assert(! requires { istringstream(1); },
+ "istringstream ctor should reject what cannot be converted to a
string_view");
+ static_assert(! requires { istringstream().str(1); },
+ "istringstream::str(s) should reject what cannot be converted to a
string_view");
+
+ static_assert(!std::is_convertible_v<std::string_view, std::istringstream>,
+ "istringstream(string_view, ios::openmode) is explicit");
+ static_assert(!std::is_convertible_v<const std::string_view,
std::istringstream>,
+ "istringstream(string_view, ios::openmode) is explicit");
+ static_assert(!std::is_convertible_v<convertible_to_string_view,
std::istringstream>,
+ "istringstream(convertible_to_string_view, ios::openmode) is explicit");
+ static_assert(!std::is_convertible_v<const convertible_to_string_view,
std::istringstream>,
+ "istringstream(convertible_to_string_view, ios::openmode) is explicit");
+
+ {
+ std::istringstream istr(cstr);
+ VERIFY( istr.str() == cstr.s );
+ VERIFY( istr.get() == cstr.s[0] );
+ }
+ {
+ std::istringstream istr(ccstr);
+ VERIFY( istr.str() == ccstr.s );
+ VERIFY( istr.get() == ccstr.s[0] );
+ }
+ {
+ std::istringstream istr(cstr, std::ios_base::in);
+ VERIFY( istr.str() == cstr.s );
+ VERIFY( istr.get() == cstr.s[0] );
+ VERIFY( istr.rdbuf()->sputc('X') != 'X' );
+ }
+ {
+ std::istringstream istr(cstr, std::ios_base::out);
+ VERIFY( istr.str() == cstr.s );
+ VERIFY( istr.get() == cstr.s[0] );
+ VERIFY( istr.rdbuf()->sputc('X') == 'X' );
+ }
+}
+
+void
+test02()
+{
+ // Test various C++26 constructors taking string views
+ // and mix of other arguments
+
+ auto const mode = std::ios_base::in | std::ios_base::out;
+
+ {
+ // template <typename T>
+ // basic_istringstream(const T&, ios_base::openmode, const allocator_type&)
+
+ std::istringstream::allocator_type a;
+ {
+ std::istringstream istr(cstr, mode, a); // ={} checks for non-explicit
ctor
+ VERIFY( istr.str() == cstr.s );
+ }
+ {
+ std::istringstream istr(cstr, std::ios::in, a);
+ VERIFY( istr.str() == cstr.s );
+ VERIFY( istr.get() == cstr.s[0] );
+ VERIFY( istr.rdbuf()->sputc('X') != 'X' );
+ }
+ {
+ std::istringstream istr(cstr, std::ios::out, a);
+ VERIFY( istr.str() == cstr.s );
+ VERIFY( istr.get() == cstr.s[0] );
+ VERIFY( istr.rdbuf()->sputc('X') == 'X' );
+ }
+ }
+
+ {
+ // template <typename T>
+ // basic_istringstream(const T&, ios_base::openmode)
+ {
+ std::istringstream istr(cstr, mode);
+ VERIFY( istr.str() == cstr.s );
+ VERIFY( istr.get() == cstr.s[0] );
+ VERIFY( istr.rdbuf()->sputc('X') == 'X' );
+ }
+ {
+ std::istringstream istr(cstr, std::ios::in);
+ VERIFY( istr.str() == cstr.s );
+ VERIFY( istr.get() == cstr.s[0] );
+ VERIFY( istr.rdbuf()->sputc('X') != 'X' );
+ }
+ {
+ std::istringstream istr(cstr, std::ios::out);
+ VERIFY( istr.str() == cstr.s );
+ VERIFY( istr.get() == cstr.s[0] );
+ VERIFY( istr.rdbuf()->sputc('X') == 'X' );
+ }
+ }
+
+ {
+ // template <typename T>
+ // explicit
+ // basic_istringstream(const T&, ios_base::openmode = ios_base::in)
+
+ std::istringstream istr(cstr);
+ VERIFY( istr.str() == cstr.s );
+ VERIFY( istr.get() == cstr.s[0] );
+ VERIFY( istr.rdbuf()->sputc('X') != 'X' );
+ }
+}
+
+using alloc_type = __gnu_test::uneq_allocator<char>;
+
+template<typename Alloc, typename C = typename Alloc::value_type>
+ using istringstream_with_alloc
+ = std::basic_istringstream<C, std::char_traits<C>, Alloc>;
+
+void test03()
+{
+ alloc_type a{1};
+ {
+ istringstream_with_alloc<alloc_type> istr(cstr, a);
+ VERIFY( istr.rdbuf()->get_allocator() == a );
+ VERIFY( std::string_view{istr.str()} == cstr );
+ VERIFY( istr.get() == cstr.s[0] );
+ }
+ {
+ istringstream_with_alloc<alloc_type> istr(cstr, std::ios::in, a);
+ VERIFY( istr.rdbuf()->get_allocator() == a );
+ VERIFY( std::string_view{istr.str()} == cstr );
+ VERIFY( istr.get() == cstr.s[0] );
+ VERIFY( istr.rdbuf()->sputc('X') != 'X' );
+ }
+ {
+ istringstream_with_alloc<alloc_type> istr(cstr, std::ios::out, a);
+ VERIFY( istr.rdbuf()->get_allocator() == a );
+ VERIFY( std::string_view{istr.str()} == cstr );
+ VERIFY( istr.get() == cstr.s[0] );
+ VERIFY( istr.rdbuf()->sputc('X') == 'X' );
+ }
+}
+
+void test04()
+{
+ {
+ std::istringstream istr;
+ istr.str( cstr );
+ VERIFY( istr.str() == cstr.s );
+ }
+ {
+ std::istringstream istr;
+ istr.str( ccstr );
+ VERIFY( istr.str() == ccstr.s );
+ }
+}
+
+int
+main()
+{
+ test01();
+ test02();
+ test03();
+ test04();
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_ostringstream/cons/char/4.cc
b/libstdc++-v3/testsuite/27_io/basic_ostringstream/cons/char/4.cc
new file mode 100644
index 00000000000..4ce335ee61f
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_ostringstream/cons/char/4.cc
@@ -0,0 +1,186 @@
+// C++26 [ostringstream.general]
+
+// { dg-do run { target c++26 } }
+// { dg-require-effective-target cxx11_abi }
+
+#include <sstream>
+#include <string>
+#include <string_view>
+#include <testsuite_allocator.h>
+#include <testsuite_hooks.h>
+
+// Check C++26 P2495 ostringstream ctors and members str(s) that accept a
+// string_view, or anything convertible to a string_view, in place of a
+// string object. Mostly just verify plumbing.
+
+struct convertible_to_string_view {
+ std::string s;
+ operator std::string_view() const { return s; }
+};
+
+const std::string str("This is a test string");
+convertible_to_string_view cstr{str}; // a copy
+const convertible_to_string_view ccstr{str}; // another copy
+
+template <typename ostringstream = std::basic_ostringstream<char>>
+void
+test01()
+{
+ // Test C++26 constructor and str(s) taking a generalized string_view
+
+ static_assert(! requires { ostringstream(1); },
+ "ostringstream ctor should reject what cannot be converted to a
string_view");
+ static_assert(! requires { ostringstream().str(1); },
+ "ostringstream::str(s) should reject what cannot be converted to a
string_view");
+
+ static_assert(!std::is_convertible_v<std::string_view, std::ostringstream>,
+ "ostringstream(string_view, ios::openmode) is explicit");
+ static_assert(!std::is_convertible_v<const std::string_view,
std::ostringstream>,
+ "ostringstream(string_view, ios::openmode) is explicit");
+ static_assert(!std::is_convertible_v<convertible_to_string_view,
std::ostringstream>,
+ "ostringstream(convertible_to_string_view, ios::openmode) is explicit");
+ static_assert(!std::is_convertible_v<const convertible_to_string_view,
std::ostringstream>,
+ "ostringstream(convertible_to_string_view, ios::openmode) is explicit");
+
+ {
+ std::ostringstream ostrstr(cstr);
+ VERIFY( ostrstr.str() == cstr.s );
+ VERIFY( ostrstr.rdbuf()->sgetc() == std::stringbuf::traits_type::eof() );
+ }
+ {
+ std::ostringstream ostrstr(ccstr);
+ VERIFY( ostrstr.str() == ccstr.s );
+ VERIFY( ostrstr.rdbuf()->sgetc() == std::stringbuf::traits_type::eof() );
+ }
+ {
+ std::ostringstream ostrstr(cstr, std::ios_base::in);
+ VERIFY( ostrstr.str() == cstr.s );
+ VERIFY( ostrstr.rdbuf()->sgetc() == cstr.s[0]);
+ VERIFY( ostrstr.put('Y').rdstate() == ostrstr.goodbit );
+ }
+ {
+ std::ostringstream ostrstr(cstr, std::ios_base::out);
+ VERIFY( ostrstr.str() == cstr.s );
+ VERIFY( ostrstr.rdbuf()->sgetc() == std::stringbuf::traits_type::eof() );
+ VERIFY( ostrstr.put('Y').rdstate() == ostrstr.goodbit );
+ }
+}
+
+void
+test02()
+{
+ // Test plumbing of C++26 various constructors taking string views
+
+ auto const mode = std::ios_base::in | std::ios_base::out;
+
+ {
+ std::ostringstream::allocator_type a;
+ // template <typename T>
+ // basic_ostringstream(const T&, ios_base::openmode, const allocator_type&)
+ {
+ std::ostringstream ostrstr(cstr, mode, a); // ={} checks for
non-explicit ctor
+ VERIFY( ostrstr.str() == cstr.s );
+ }
+ {
+ std::ostringstream ostrstr(cstr, std::ios::in, a);
+ VERIFY( ostrstr.str() == cstr.s );
+ VERIFY( ostrstr.rdbuf()->sgetc() == cstr.s[0]);
+ VERIFY( ostrstr.put('Y').rdstate() == ostrstr.goodbit );
+ }
+ {
+ std::ostringstream ostrstr(cstr, std::ios::out, a);
+ VERIFY( ostrstr.str() == cstr.s );
+ VERIFY( ostrstr.rdbuf()->sgetc() == std::stringbuf::traits_type::eof() );
+ VERIFY( ostrstr.put('Y').rdstate() == ostrstr.goodbit );
+ }
+ }
+
+ {
+ // template <typename T>
+ // basic_ostringstream(const T&, ios_base::openmode)
+ {
+ std::ostringstream ostrstr(cstr, mode);
+ VERIFY( ostrstr.str() == cstr.s );
+ VERIFY( ostrstr.rdbuf()->sgetc() == cstr.s[0]);
+ VERIFY( ostrstr.put('Y').good() );
+ }
+ {
+ std::ostringstream ostrstr(cstr, std::ios::in);
+ VERIFY( ostrstr.str() == cstr.s );
+ VERIFY( ostrstr.rdbuf()->sgetc() == cstr.s[0]);
+ VERIFY( ostrstr.put('X').good() );
+ }
+ {
+ std::ostringstream ostrstr(cstr, std::ios::out);
+ VERIFY( ostrstr.str() == cstr.s );
+ VERIFY( ostrstr.rdbuf()->sgetc() == std::stringbuf::traits_type::eof() );
+ VERIFY( ostrstr.put('Y').rdstate() == ostrstr.goodbit );
+ }
+ }
+
+ {
+ // template <typename T>
+ // explicit
+ // basic_ostringstream(const T&, ios_base::openmode = ios_base::out)
+
+ std::ostringstream ostrstr(cstr);
+ VERIFY( ostrstr.str() == cstr.s );
+ VERIFY( ostrstr.rdbuf()->sgetc() == std::stringbuf::traits_type::eof() );
+ VERIFY( ostrstr.put('Y').good() );
+ }
+}
+
+using alloc_type = __gnu_test::uneq_allocator<char>;
+
+template<typename Alloc, typename C = typename Alloc::value_type>
+ using ostringstream_with_alloc
+ = std::basic_ostringstream<C, std::char_traits<C>, Alloc>;
+
+void test03()
+{
+ alloc_type a{1};
+ {
+ ostringstream_with_alloc<alloc_type> ostrstr(cstr, a);
+ VERIFY( ostrstr.rdbuf()->get_allocator() == a );
+ VERIFY( std::string_view{ostrstr.str()} == cstr );
+ VERIFY( ostrstr.rdbuf()->sgetc() == std::stringbuf::traits_type::eof() );
+ VERIFY( ostrstr.put('X').good() );
+ }
+ {
+ ostringstream_with_alloc<alloc_type> ostrstr(cstr, std::ios::in, a);
+ VERIFY( ostrstr.rdbuf()->get_allocator() == a );
+ VERIFY( std::string_view{ostrstr.str()} == cstr );
+ VERIFY( ostrstr.rdbuf()->sgetc() == cstr.s[0]);
+ VERIFY( ostrstr.put('X').good() );
+ }
+ {
+ ostringstream_with_alloc<alloc_type> ostrstr(cstr, std::ios::out, a);
+ VERIFY( ostrstr.rdbuf()->get_allocator() == a );
+ VERIFY( std::string_view{ostrstr.str()} == cstr );
+ VERIFY( ostrstr.rdbuf()->sgetc() == std::stringbuf::traits_type::eof() );
+ VERIFY( ostrstr.put('Y').rdstate() == ostrstr.goodbit );
+ }
+}
+
+void test04()
+{
+ {
+ std::ostringstream ostrstr;
+ ostrstr.str(cstr);
+ VERIFY( ostrstr.str() == cstr.s );
+ }
+ {
+ std::ostringstream ostrstr;
+ ostrstr.str(ccstr);
+ VERIFY( ostrstr.str() == ccstr.s );
+ }
+}
+
+int
+main()
+{
+ test01();
+ test02();
+ test03();
+ test04();
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/char/3.cc
b/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/char/3.cc
new file mode 100644
index 00000000000..335b3082465
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/char/3.cc
@@ -0,0 +1,196 @@
+// C++26 31.8.2.1 [stringbuf.general]
+
+// { dg-do run { target c++26 } }
+// { dg-require-effective-target cxx11_abi }
+
+#include <sstream>
+#include <string>
+#include <string_view>
+#include <testsuite_allocator.h>
+#include <testsuite_hooks.h>
+
+// Check C++26 P2495 stringbuf ctors and members str(s) that accept a
+// string_view, or anything convertible to a string_view, in place of a
+// string object.
+
+struct convertible_to_string_view {
+ std::string s;
+ operator std::string_view() const { return s; }
+};
+
+const std::string str("This is a test string");
+convertible_to_string_view cstr{str}; // a copy
+const convertible_to_string_view ccstr{str}; // another copy
+
+template <typename stringbuf = std::basic_stringbuf<char>>
+void
+test01()
+{
+ // Test C++26 constructor and str(s) taking a generalized string_view
+
+ static_assert(! requires { stringbuf(1); },
+ "stringbuf ctor should reject what cannot be converted to a
string_view");
+ static_assert(! requires { stringbuf().str(1); },
+ "stringbuf::str(s) should reject what cannot be converted to a
string_view");
+
+ static_assert(!std::is_convertible_v<std::string_view, std::stringbuf>,
+ "stringbuf(string_view, ios::openmode) is explicit");
+ static_assert(!std::is_convertible_v<const std::string_view, std::stringbuf>,
+ "stringbuf(string_view, ios::openmode) is explicit");
+ static_assert(!std::is_convertible_v<convertible_to_string_view,
std::stringbuf>,
+ "stringbuf(convertible_to_string_view, ios::openmode) is explicit");
+ static_assert(!std::is_convertible_v<const convertible_to_string_view,
std::stringbuf>,
+ "stringbuf(convertible_to_string_view, ios::openmode) is explicit");
+
+ {
+ std::stringbuf sbuf(cstr);
+ VERIFY( sbuf.str() == cstr.s );
+ VERIFY( sbuf.sgetc() == cstr.s[0] );
+ }
+ {
+ std::stringbuf sbuf(ccstr);
+ VERIFY( sbuf.str() == ccstr.s );
+ VERIFY( sbuf.sgetc() == ccstr.s[0] );
+ }
+ {
+ std::stringbuf sbuf(cstr, std::ios_base::in);
+ VERIFY( sbuf.str() == cstr.s );
+ VERIFY( sbuf.sgetc() == cstr.s[0] );
+ VERIFY( sbuf.sputc('X') == std::stringbuf::traits_type::eof() );
+ }
+ {
+ std::stringbuf sbuf(ccstr, std::ios_base::in);
+ VERIFY( sbuf.str() == ccstr.s );
+ VERIFY( sbuf.sgetc() == ccstr.s[0] );
+ VERIFY( sbuf.sputc('X') == std::stringbuf::traits_type::eof() );
+ }
+ {
+ std::stringbuf sbuf(cstr, std::ios_base::out);
+ VERIFY( sbuf.str() == cstr.s );
+ VERIFY( sbuf.sputc('Y') == 'Y' );
+ VERIFY( sbuf.sgetc() == std::stringbuf::traits_type::eof() );
+ }
+ {
+ std::stringbuf sbuf(ccstr, std::ios_base::out);
+ VERIFY( sbuf.str() == ccstr.s );
+ VERIFY( sbuf.sputc('Y') == 'Y' );
+ VERIFY( sbuf.sgetc() == std::stringbuf::traits_type::eof() );
+ }
+}
+
+void
+test02()
+{
+ // Test C++26 constructors taking string views using different allocators
+
+ auto const mode = std::ios_base::in | std::ios_base::out;
+
+ {
+ // template <typename T>
+ // basic_stringbuf(const T&, ios_base::openmode, const allocator_type&)
+
+ std::stringbuf::allocator_type a;
+ {
+ std::stringbuf sbuf(cstr, mode, a); // ={} checks for non-explicit ctor
+ VERIFY( sbuf.str() == cstr.s );
+ }
+ {
+ std::stringbuf sbuf(cstr, std::ios::in, a);
+ VERIFY( sbuf.str() == cstr.s );
+ VERIFY( sbuf.sgetc() == cstr.s[0] );
+ VERIFY( sbuf.sputc('X') == std::stringbuf::traits_type::eof() );
+ }
+
+ {
+ std::stringbuf sbuf(cstr, std::ios::out, a);
+ VERIFY( sbuf.str() == cstr.s );
+ VERIFY( sbuf.sputc('X') == 'X' );
+ VERIFY( sbuf.sgetc() == std::stringbuf::traits_type::eof() );
+ }
+ }
+
+ {
+ // template <typename T>
+ // basic_stringbuf(const T&, ios_base::openmode)
+ {
+ std::stringbuf sbuf(cstr, mode);
+ VERIFY( sbuf.str() == cstr.s );
+ }
+ {
+ std::stringbuf sbuf(cstr, std::ios::in);
+ VERIFY( sbuf.str() == cstr.s );
+ VERIFY( sbuf.sgetc() == cstr.s[0] );
+ VERIFY( sbuf.sputc('X') == std::stringbuf::traits_type::eof() );
+ }
+ {
+ std::stringbuf sbuf(cstr, std::ios::out);
+ VERIFY( sbuf.str() == cstr.s );
+ VERIFY( sbuf.sputc('X') == 'X' );
+ VERIFY( sbuf.sgetc() == std::stringbuf::traits_type::eof() );
+ }
+ }
+
+ {
+ // template <typename T>
+ // explicit
+ // basic_stringbuf(const T&, ios_base::openmode =
ios_base::in|ios_base::out)
+
+ std::stringbuf sbuf(cstr);
+ VERIFY( sbuf.str() == cstr.s );
+ VERIFY( sbuf.sgetc() == cstr.s[0] );
+ }
+}
+
+using alloc_type = __gnu_test::uneq_allocator<char>;
+
+template<typename Alloc, typename C = typename Alloc::value_type>
+ using stringbuf_with_alloc
+ = std::basic_stringbuf<C, std::char_traits<C>, Alloc>;
+
+void test03()
+{
+ alloc_type a{1};
+ {
+ stringbuf_with_alloc<alloc_type> sbuf(cstr, a);
+ VERIFY( sbuf.get_allocator() == a );
+ VERIFY( std::string_view{sbuf.str()} == cstr );
+ VERIFY( sbuf.sgetc() == cstr.s[0] );
+ }
+ {
+ stringbuf_with_alloc<alloc_type> sbuf(cstr, std::ios::in, a);
+ VERIFY( sbuf.get_allocator() == a );
+ VERIFY( std::string_view{sbuf.str()} == cstr );
+ VERIFY( sbuf.sgetc() == cstr.s[0] );
+ VERIFY( sbuf.sputc('X') == std::stringbuf::traits_type::eof() );
+ }
+ {
+ stringbuf_with_alloc<alloc_type> sbuf(cstr, std::ios::out, a);
+ VERIFY( sbuf.get_allocator() == a );
+ VERIFY( std::string_view{sbuf.str()} == cstr );
+ VERIFY( sbuf.sputc('X') == 'X' );
+ VERIFY( sbuf.sgetc() == std::stringbuf::traits_type::eof() );
+ }
+}
+
+void test04()
+{
+ {
+ std::stringbuf sbuf;
+ sbuf.str(cstr);
+ VERIFY( sbuf.str() == cstr.s );
+ }
+ {
+ std::stringbuf sbuf;
+ sbuf.str(ccstr);
+ VERIFY( sbuf.str() == ccstr.s );
+ }
+}
+
+int
+main()
+{
+ test01();
+ test02();
+ test03();
+ test04();
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_stringstream/cons/char/2.cc
b/libstdc++-v3/testsuite/27_io/basic_stringstream/cons/char/2.cc
new file mode 100644
index 00000000000..ad463df4bfa
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_stringstream/cons/char/2.cc
@@ -0,0 +1,196 @@
+// C++26 31.8.2.1 [stringstream.general]
+
+// { dg-do run { target c++26 } }
+// { dg-require-effective-target cxx11_abi }
+
+#include <sstream>
+#include <string>
+#include <string_view>
+#include <testsuite_allocator.h>
+#include <testsuite_hooks.h>
+
+// Check C++26 P2495 stringstream ctors and members str(s) that accept a
+// string_view, or anything convertible to a string_view, in place of a
+// string object. Mostly just verify plumbing.
+
+struct convertible_to_string_view {
+ std::string s;
+ operator std::string_view() const { return s; }
+};
+
+const std::string str("This is a test string");
+convertible_to_string_view cstr{str}; // a copy
+const convertible_to_string_view ccstr{str}; // another copy
+
+template <typename stringstream = std::basic_stringstream<char>>
+void
+test01()
+{
+ // Test C++26 constructor and str(s) taking a generalized string_view
+
+ static_assert(! requires { stringstream(1); },
+ "stringstream ctor should reject what cannot be converted to a
string_view");
+ static_assert(! requires { stringstream().str(1); },
+ "stringstream::str(s) should reject what cannot be converted to a
string_view");
+
+ static_assert(!std::is_convertible_v<std::string_view, std::stringstream>,
+ "stringstream(string_view, ios::openmode) is explicit");
+ static_assert(!std::is_convertible_v<const std::string_view,
std::stringstream>,
+ "stringstream(string_view, ios::openmode) is explicit");
+ static_assert(!std::is_convertible_v<convertible_to_string_view,
std::stringstream>,
+ "stringstream(convertible_to_string_view, ios::openmode) is explicit");
+ static_assert(!std::is_convertible_v<const convertible_to_string_view,
std::stringstream>,
+ "stringstream(convertible_to_string_view, ios::openmode) is explicit");
+
+ {
+ std::stringstream strstr(cstr);
+ VERIFY( strstr.str() == cstr.s );
+ VERIFY( strstr.get() == cstr.s[0] );
+ }
+ {
+ std::stringstream strstr(ccstr);
+ VERIFY( strstr.str() == ccstr.s );
+ VERIFY( strstr.get() == ccstr.s[0] );
+ }
+ {
+ std::stringstream strstr(cstr, std::ios_base::in);
+ VERIFY( strstr.str() == cstr.s );
+ VERIFY( strstr.get() == cstr.s[0] );
+ VERIFY( strstr.put('X').rdstate() == strstr.badbit );
+ }
+ {
+ std::stringstream strstr(cstr, std::ios_base::out);
+ VERIFY( strstr.str() == cstr.s );
+ VERIFY( strstr.put('Y').good() );
+ VERIFY( strstr.get() == std::stringbuf::traits_type::eof());
+ }
+}
+
+void
+test02()
+{
+ // Test C++26 various constructors taking string views
+
+ auto const mode = std::ios_base::in | std::ios_base::out;
+
+ {
+ // template <typename T>
+ // basic_stringstream(const T&, ios_base::openmode, const allocator_type&)
+
+ std::stringstream::allocator_type a;
+ {
+ std::stringstream strstr(cstr, mode, a); // ={} checks for non-explicit
ctor
+ VERIFY( strstr.str() == cstr.s );
+ }
+ {
+ std::stringstream strstr(cstr, std::ios::in, a);
+ VERIFY( strstr.str() == cstr.s );
+ VERIFY( strstr.get() == cstr.s[0] );
+ VERIFY( strstr.put('X').rdstate() == strstr.badbit );
+ }
+ {
+ std::stringstream strstr(cstr, std::ios::out, a);
+ VERIFY( strstr.str() == cstr.s );
+ VERIFY( strstr.put('X').good() );
+ VERIFY( strstr.get() == std::stringbuf::traits_type::eof());
+ }
+ }
+
+ {
+ // template <typename T>
+ // basic_stringstream(const T&, ios_base::openmode)
+
+ {
+ std::stringstream strstr(cstr, mode);
+ VERIFY( strstr.str() == cstr.s );
+ VERIFY( strstr.get() == cstr.s[0] );
+ VERIFY( strstr.put('X').good() );
+ }
+ {
+ std::stringstream strstr(cstr, std::ios::in);
+ VERIFY( strstr.str() == cstr.s );
+ VERIFY( strstr.get() == cstr.s[0] );
+ VERIFY( strstr.put('X').rdstate() == strstr.badbit );
+ }
+ {
+ std::stringstream strstr(cstr, std::ios::out);
+ VERIFY( strstr.str() == cstr.s );
+ VERIFY( strstr.put('X').good() );
+ VERIFY( strstr.get() == std::stringbuf::traits_type::eof());
+ }
+ }
+
+ {
+ // template <typename T>
+ // explicit
+ // basic_stringstream(const T&, ios_base::openmode =
ios_base::in|ios_base::out)
+
+ std::stringstream strstr(cstr);
+ VERIFY( strstr.str() == cstr.s );
+ VERIFY( strstr.get() == cstr.s[0] );
+ VERIFY( strstr.put('X').good() );
+ }
+}
+
+// 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) { }
+ };
+
+using alloc_type = __gnu_test::uneq_allocator<char>;
+
+template<typename Alloc, typename C = typename Alloc::value_type>
+ using stringstream_with_alloc
+ = std::basic_stringstream<C, std::char_traits<C>, Alloc>;
+
+void test03()
+{
+ alloc_type a{1};
+ {
+ stringstream_with_alloc<alloc_type> strstr(cstr, a);
+ VERIFY( strstr.rdbuf()->get_allocator() == a );
+ VERIFY( std::string_view{strstr.str()} == cstr );
+ VERIFY( strstr.get() == cstr.s[0] );
+ }
+ {
+ stringstream_with_alloc<alloc_type> strstr(cstr, std::ios::in, a);
+ VERIFY( strstr.rdbuf()->get_allocator() == a );
+ VERIFY( std::string_view{strstr.str()} == cstr );
+ VERIFY( strstr.get() == cstr.s[0] );
+ VERIFY( strstr.put('X').rdstate() == strstr.badbit );
+ }
+ {
+ stringstream_with_alloc<alloc_type> strstr(cstr, std::ios::out, a);
+ VERIFY( strstr.rdbuf()->get_allocator() == a );
+ VERIFY( std::string_view{strstr.str()} == cstr );
+ VERIFY( strstr.put('X').good() );
+ VERIFY( strstr.get() == std::stringbuf::traits_type::eof());
+ }
+}
+
+void test04()
+{
+ {
+ std::stringstream strstr;
+ strstr.str( cstr );
+ VERIFY( strstr.str() == cstr.s );
+ }
+ {
+ std::stringstream strstr;
+ strstr.str( ccstr );
+ VERIFY( strstr.str() == ccstr.s );
+ }
+}
+
+int
+main()
+{
+ test01();
+ test02();
+ test03();
+ test04();
+}
--
2.49.0