On Thu, May 29, 2025 at 9:21 AM Nathan Myers <n...@cantrip.org> wrote:

> 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.
>
Could you add a note that we are not adding, because it is redundant?
https://eel.is/c++draft/istringstream.cons#6


>
> libstdc++-v3/ChangeLog:
>
>         PR libstdc++/119741
>
Remove this line.

>         * 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.
>
These tests are failing for me. Most likely due feature test macros, see
below.

> ---
>  libstdc++-v3/include/bits/version.def         |  11 +-
>  libstdc++-v3/include/bits/version.h           |  10 +
>  libstdc++-v3/include/std/sstream              | 174 ++++++++++++++--
>  .../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, 941 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;
> +    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..f019eb1e9fc 100644
> --- a/libstdc++-v3/include/std/sstream
> +++ b/libstdc++-v3/include/std/sstream
> @@ -38,9 +38,14 @@
>  #endif
>
>  #include <bits/requires_hosted.h> // iostream
>
Add #define __glibcxx_want_sstream_from_string_view here, so the macro is
exposed by this header.
Also add a test for feature test macro looking like this example here:
#include <sstream>
#ifndef __cpp_lib_indirect
# error __cpp_lib_indirect feature test macro missing in <memory>
#elif __cpp_lib_indirect != 202502
# error __cpp_lib_indirect feature test macro has wrong value in <memory>
#endif
And then other includes.

+#include <bits/version.h>
>
>  #include <istream>
>  #include <ostream>
> +#ifdef __cpp_lib_sstream_from_string_view
>
Without  the __want macro, this would not be defined, and corresponding
function would not be accessible.
Also I think I would prefer to use internal
`__glibcxx_want_sstream_from_string_view` that is always provided
by bits/version.h/

> +# include <type_traits>  // is_convertible_v
> +#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,31 @@ _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 +290,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 +346,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 +365,18 @@ _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 +563,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 +628,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 +645,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 +664,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 +681,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)); }
> @@ -671,6 +716,26 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
>         { }
>  #endif // C++20
>
> +#ifdef __cpp_lib_sstream_from_string_view
> +      template <typename _Tp>
> +       explicit basic_istringstream(const _Tp& __t, 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 +767,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 +781,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 +813,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 +825,14 @@ _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 +887,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 +904,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 +923,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 +940,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 +975,26 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
>         { }
>  #endif // C++20
>
> +#ifdef __cpp_lib_sstream_from_string_view
> +      template <typename _Tp>
> +       explicit basic_ostringstream(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 +1026,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 +1040,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 +1072,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 +1084,14 @@ _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 +1146,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 +1161,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 +1178,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 +1195,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 +1232,27 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
>         { }
>  #endif // C++20
>
> +#ifdef __cpp_lib_sstream_from_string_view
> +      template <typename _Tp>
> +       explicit basic_stringstream(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_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 +1284,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 +1298,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 +1330,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 +1342,14 @@ _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 }
> +
> +#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
>
>

Reply via email to