libstdc++: sstream from string_view (P2495R3) [PR119741]

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 ctors and function take a string.
Note this change omits the constraint applied to the istringstream constructor
from string cited as a "drive-by" in P2495R3, as we have determined it is
redundant.

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/string_view.cc:
	New tests.
	* testsuite/27_io/basic_istringstream/cons/char/string_view.cc:
	New tests.
	* testsuite/27_io/basic_ostringstream/cons/char/string_view.cc:
	New tests.
	* testsuite/27_io/basic_stringstream/cons/char/string_view.cc:
	New tests.
	* testsuite/27_io/basic_stringbuf/cons/wchar_t/string_view.cc:
	New tests.
	* testsuite/27_io/basic_istringstream/cons/wchar_t/string_view.cc:
	New tests.
	* testsuite/27_io/basic_ostringstream/cons/wchar_t/string_view.cc:
	New tests.
	* testsuite/27_io/basic_stringstream/cons/wchar_t/string_view.cc:
	New tests.

Reviewed-by: Jonathan Wakely
This commit is contained in:
Nathan Myers
2025-06-04 14:52:29 -04:00
parent cdfa5fe035
commit 845826088c
11 changed files with 1026 additions and 19 deletions

View File

@@ -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;
@@ -1995,6 +1995,15 @@ ftms = {
};
};
ftms = {
name = sstream_from_string_view;
values = {
v = 202306;
cxxmin = 26;
hosted = yes;
};
};
// Standard test specifications.
stds[97] = ">= 199711L";
stds[03] = ">= 199711L";

View File

@@ -2233,4 +2233,14 @@
#endif /* !defined(__cpp_lib_polymorphic) && defined(__glibcxx_want_polymorphic) */
#undef __glibcxx_want_polymorphic
#if !defined(__cpp_lib_sstream_from_string_view)
# if (__cplusplus > 202302L) && _GLIBCXX_HOSTED
# define __glibcxx_sstream_from_string_view 202306L
# if defined(__glibcxx_want_all) || defined(__glibcxx_want_sstream_from_string_view)
# define __cpp_lib_sstream_from_string_view 202306L
# 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

View File

@@ -41,8 +41,16 @@
#include <istream>
#include <ostream>
#include <bits/alloc_traits.h> // allocator_traits, __allocator_like
#define __glibcxx_want_sstream_from_string_view
#include <bits/version.h>
#ifdef __cpp_lib_sstream_from_string_view
# include <string_view>
#endif
#if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI
# define _GLIBCXX_LVAL_REF_QUAL &
# define _GLIBCXX_SSTREAM_ALWAYS_INLINE
@@ -52,8 +60,6 @@
# define _GLIBCXX_SSTREAM_ALWAYS_INLINE [[__gnu__::__always_inline__]]
#endif
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
@@ -159,6 +165,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 +204,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 +298,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 +354,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 +373,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 +572,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 +637,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 +654,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 +673,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 +690,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 +725,32 @@ _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 +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,32 @@ _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 +1048,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 +1062,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 +1094,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 +1106,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 +1169,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 +1184,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 +1201,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 +1218,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 +1255,31 @@ _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 +1311,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 +1325,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 +1357,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 +1369,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

View File

@@ -0,0 +1,195 @@
// C++26 [istringstream.general]
// { dg-do run { target c++26 } }
#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.
#ifndef C
# define C char
# define L(a) a
#endif
using string = std::basic_string<C>;
using string_view = std::basic_string_view<C>;
using istringstream = std::basic_istringstream<C>;
struct convertible_to_string_view {
string s;
operator string_view() const { return s; }
};
const string str(L("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<C>>
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<string_view, istringstream>,
"istringstream(string_view, ios::openmode) is explicit");
static_assert(!std::is_convertible_v<const string_view, istringstream>,
"istringstream(string_view, ios::openmode) is explicit");
static_assert(!std::is_convertible_v<convertible_to_string_view, istringstream>,
"istringstream(convertible_to_string_view, ios::openmode) is explicit");
static_assert(!std::is_convertible_v<const convertible_to_string_view, istringstream>,
"istringstream(convertible_to_string_view, ios::openmode) is explicit");
{
istringstream istr(cstr);
VERIFY( istr.str() == cstr.s );
VERIFY( istr.get() == cstr.s[0] );
}
{
istringstream istr(ccstr);
VERIFY( istr.str() == ccstr.s );
VERIFY( istr.get() == ccstr.s[0] );
}
{
istringstream istr(cstr, std::ios_base::in);
VERIFY( istr.str() == cstr.s );
VERIFY( istr.get() == cstr.s[0] );
VERIFY( istr.rdbuf()->sputc('X') != 'X' );
}
{
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&)
istringstream::allocator_type a;
{
istringstream istr(cstr, mode, a); // ={} checks for non-explicit ctor
VERIFY( istr.str() == cstr.s );
}
{
istringstream istr(cstr, std::ios::in, a);
VERIFY( istr.str() == cstr.s );
VERIFY( istr.get() == cstr.s[0] );
VERIFY( istr.rdbuf()->sputc('X') != 'X' );
}
{
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)
{
istringstream istr(cstr, mode);
VERIFY( istr.str() == cstr.s );
VERIFY( istr.get() == cstr.s[0] );
VERIFY( istr.rdbuf()->sputc('X') == 'X' );
}
{
istringstream istr(cstr, std::ios::in);
VERIFY( istr.str() == cstr.s );
VERIFY( istr.get() == cstr.s[0] );
VERIFY( istr.rdbuf()->sputc('X') != 'X' );
}
{
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)
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<C>;
template<typename Alloc, typename CC = typename Alloc::value_type>
using istringstream_with_alloc
= std::basic_istringstream<CC, std::char_traits<CC>, Alloc>;
void test03()
{
alloc_type a{1};
{
istringstream_with_alloc<alloc_type> istr(cstr, a);
VERIFY( istr.rdbuf()->get_allocator() == a );
VERIFY( 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( 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( string_view{istr.str()} == cstr );
VERIFY( istr.get() == cstr.s[0] );
VERIFY( istr.rdbuf()->sputc('X') == 'X' );
}
}
void test04()
{
{
istringstream istr;
istr.str( cstr );
VERIFY( istr.str() == cstr.s );
}
{
istringstream istr;
istr.str( ccstr );
VERIFY( istr.str() == ccstr.s );
}
}
int
main()
{
test01();
test02();
test03();
test04();
}

View File

@@ -0,0 +1,6 @@
// C++26 [istringstream.general]
// { dg-do run { target c++26 } }
#define C wchar_t
#define L(a) L##a
#include "../char/string_view.cc"

View File

@@ -0,0 +1,194 @@
// C++26 [ostringstream.general]
// { dg-do run { target c++26 } }
#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.
#ifndef C
# define C char
# define L(a) a
#endif
using string = std::basic_string<C>;
using string_view = std::basic_string_view<C>;
using ostringstream = std::basic_ostringstream<C>;
struct convertible_to_string_view {
string s;
operator string_view() const { return s; }
};
const string str(L("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<C>>
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<string_view, ostringstream>,
"ostringstream(string_view, ios::openmode) is explicit");
static_assert(!std::is_convertible_v<const string_view, ostringstream>,
"ostringstream(string_view, ios::openmode) is explicit");
static_assert(!std::is_convertible_v<convertible_to_string_view, ostringstream>,
"ostringstream(convertible_to_string_view, ios::openmode) is explicit");
static_assert(!std::is_convertible_v<const convertible_to_string_view, ostringstream>,
"ostringstream(convertible_to_string_view, ios::openmode) is explicit");
{
ostringstream ostrstr(cstr);
VERIFY( ostrstr.str() == cstr.s );
VERIFY( ostrstr.rdbuf()->sgetc() == ostringstream::traits_type::eof() );
}
{
ostringstream ostrstr(ccstr);
VERIFY( ostrstr.str() == ccstr.s );
VERIFY( ostrstr.rdbuf()->sgetc() == ostringstream::traits_type::eof() );
}
{
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 );
}
{
ostringstream ostrstr(cstr, std::ios_base::out);
VERIFY( ostrstr.str() == cstr.s );
VERIFY( ostrstr.rdbuf()->sgetc() == ostringstream::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;
{
ostringstream::allocator_type a;
// template <typename T>
// basic_ostringstream(const T&, ios_base::openmode, const allocator_type&)
{
ostringstream ostrstr(cstr, mode, a); // ={} checks for non-explicit ctor
VERIFY( ostrstr.str() == cstr.s );
}
{
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 );
}
{
ostringstream ostrstr(cstr, std::ios::out, a);
VERIFY( ostrstr.str() == cstr.s );
VERIFY( ostrstr.rdbuf()->sgetc() == ostringstream::traits_type::eof() );
VERIFY( ostrstr.put('Y').rdstate() == ostrstr.goodbit );
}
}
{
// template <typename T>
// basic_ostringstream(const T&, ios_base::openmode)
{
ostringstream ostrstr(cstr, mode);
VERIFY( ostrstr.str() == cstr.s );
VERIFY( ostrstr.rdbuf()->sgetc() == cstr.s[0]);
VERIFY( ostrstr.put('Y').good() );
}
{
ostringstream ostrstr(cstr, std::ios::in);
VERIFY( ostrstr.str() == cstr.s );
VERIFY( ostrstr.rdbuf()->sgetc() == cstr.s[0]);
VERIFY( ostrstr.put('X').good() );
}
{
ostringstream ostrstr(cstr, std::ios::out);
VERIFY( ostrstr.str() == cstr.s );
VERIFY( ostrstr.rdbuf()->sgetc() == ostringstream::traits_type::eof() );
VERIFY( ostrstr.put('Y').rdstate() == ostrstr.goodbit );
}
}
{
// template <typename T>
// explicit
// basic_ostringstream(const T&, ios_base::openmode = ios_base::out)
ostringstream ostrstr(cstr);
VERIFY( ostrstr.str() == cstr.s );
VERIFY( ostrstr.rdbuf()->sgetc() == ostringstream::traits_type::eof() );
VERIFY( ostrstr.put('Y').good() );
}
}
using alloc_type = __gnu_test::uneq_allocator<C>;
template<typename Alloc, typename CC = typename Alloc::value_type>
using ostringstream_with_alloc
= std::basic_ostringstream<CC, std::char_traits<CC>, Alloc>;
void test03()
{
alloc_type a{1};
{
ostringstream_with_alloc<alloc_type> ostrstr(cstr, a);
VERIFY( ostrstr.rdbuf()->get_allocator() == a );
VERIFY( string_view{ostrstr.str()} == cstr );
VERIFY( ostrstr.rdbuf()->sgetc() == ostringstream::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( 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( string_view{ostrstr.str()} == cstr );
VERIFY( ostrstr.rdbuf()->sgetc() == ostringstream::traits_type::eof() );
VERIFY( ostrstr.put('Y').rdstate() == ostrstr.goodbit );
}
}
void test04()
{
{
ostringstream ostrstr;
ostrstr.str(cstr);
VERIFY( ostrstr.str() == cstr.s );
}
{
ostringstream ostrstr;
ostrstr.str(ccstr);
VERIFY( ostrstr.str() == ccstr.s );
}
}
int
main()
{
test01();
test02();
test03();
test04();
}

View File

@@ -0,0 +1,6 @@
// C++26 [ostringstream.general]
// { dg-do run { target c++26 } }
#define C wchar_t
#define L(a) L##a
#include "../char/string_view.cc"

View File

@@ -0,0 +1,205 @@
// C++26 31.8.2.1 [stringbuf.general]
// { dg-do run { target c++26 } }
#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.
#ifndef C
# define C char
# define L(a) a
#endif
using string = std::basic_string<C>;
using string_view = std::basic_string_view<C>;
using stringbuf = std::basic_stringbuf<C>;
struct convertible_to_string_view {
string s;
operator string_view() const { return s; }
};
const string str(L("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<C>>
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<string_view, stringbuf>,
"stringbuf(string_view, ios::openmode) is explicit");
static_assert(!std::is_convertible_v<const string_view, stringbuf>,
"stringbuf(string_view, ios::openmode) is explicit");
static_assert(!std::is_convertible_v<convertible_to_string_view, stringbuf>,
"stringbuf(convertible_to_string_view, ios::openmode) is explicit");
static_assert(
!std::is_convertible_v<const convertible_to_string_view, stringbuf>,
"stringbuf(convertible_to_string_view, ios::openmode) is explicit");
{
stringbuf sbuf(cstr);
VERIFY( sbuf.str() == cstr.s );
VERIFY( sbuf.sgetc() == cstr.s[0] );
}
{
stringbuf sbuf(ccstr);
VERIFY( sbuf.str() == ccstr.s );
VERIFY( sbuf.sgetc() == ccstr.s[0] );
}
{
stringbuf sbuf(cstr, std::ios_base::in);
VERIFY( sbuf.str() == cstr.s );
VERIFY( sbuf.sgetc() == cstr.s[0] );
VERIFY( sbuf.sputc('X') == stringbuf::traits_type::eof() );
}
{
stringbuf sbuf(ccstr, std::ios_base::in);
VERIFY( sbuf.str() == ccstr.s );
VERIFY( sbuf.sgetc() == ccstr.s[0] );
VERIFY( sbuf.sputc('X') == stringbuf::traits_type::eof() );
}
{
stringbuf sbuf(cstr, std::ios_base::out);
VERIFY( sbuf.str() == cstr.s );
VERIFY( sbuf.sputc('Y') == 'Y' );
VERIFY( sbuf.sgetc() == stringbuf::traits_type::eof() );
}
{
stringbuf sbuf(ccstr, std::ios_base::out);
VERIFY( sbuf.str() == ccstr.s );
VERIFY( sbuf.sputc('Y') == 'Y' );
VERIFY( sbuf.sgetc() == 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&)
stringbuf::allocator_type a;
{
stringbuf sbuf(cstr, mode, a); // ={} checks for non-explicit ctor
VERIFY( sbuf.str() == cstr.s );
}
{
stringbuf sbuf(cstr, std::ios::in, a);
VERIFY( sbuf.str() == cstr.s );
VERIFY( sbuf.sgetc() == cstr.s[0] );
VERIFY( sbuf.sputc('X') == stringbuf::traits_type::eof() );
}
{
stringbuf sbuf(cstr, std::ios::out, a);
VERIFY( sbuf.str() == cstr.s );
VERIFY( sbuf.sputc('X') == 'X' );
VERIFY( sbuf.sgetc() == stringbuf::traits_type::eof() );
}
}
{
// template <typename T>
// basic_stringbuf(const T&, ios_base::openmode)
{
stringbuf sbuf(cstr, mode);
VERIFY( sbuf.str() == cstr.s );
}
{
stringbuf sbuf(cstr, std::ios::in);
VERIFY( sbuf.str() == cstr.s );
VERIFY( sbuf.sgetc() == cstr.s[0] );
VERIFY( sbuf.sputc('X') == stringbuf::traits_type::eof() );
}
{
stringbuf sbuf(cstr, std::ios::out);
VERIFY( sbuf.str() == cstr.s );
VERIFY( sbuf.sputc('X') == 'X' );
VERIFY( sbuf.sgetc() == stringbuf::traits_type::eof() );
}
}
{
// template <typename T>
// explicit
// basic_stringbuf(const T&, ios_base::openmode = ios_base::in|ios_base::out)
stringbuf sbuf(cstr);
VERIFY( sbuf.str() == cstr.s );
VERIFY( sbuf.sgetc() == cstr.s[0] );
}
}
using alloc_type = __gnu_test::uneq_allocator<C>;
template<typename Alloc, typename CC = typename Alloc::value_type>
using stringbuf_with_alloc
= std::basic_stringbuf<CC, std::char_traits<CC>, Alloc>;
void test03()
{
alloc_type a{1};
{
stringbuf_with_alloc<alloc_type> sbuf(cstr, a);
VERIFY( sbuf.get_allocator() == a );
VERIFY( 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( string_view{sbuf.str()} == cstr );
VERIFY( sbuf.sgetc() == cstr.s[0] );
VERIFY( sbuf.sputc('X') == stringbuf::traits_type::eof() );
}
{
stringbuf_with_alloc<alloc_type> sbuf(cstr, std::ios::out, a);
VERIFY( sbuf.get_allocator() == a );
VERIFY( string_view{sbuf.str()} == cstr );
VERIFY( sbuf.sputc('X') == 'X' );
VERIFY( sbuf.sgetc() == stringbuf::traits_type::eof() );
}
}
void test04()
{
{
stringbuf sbuf;
sbuf.str(cstr);
VERIFY( sbuf.str() == cstr.s );
}
{
stringbuf sbuf;
sbuf.str(ccstr);
VERIFY( sbuf.str() == ccstr.s );
}
}
int
main()
{
test01();
test02();
test03();
test04();
}

View File

@@ -0,0 +1,6 @@
// C++26 [stringbuf.general]
// { dg-do run { target c++26 } }
#define C wchar_t
#define L(a) L##a
#include "../char/string_view.cc"

View File

@@ -0,0 +1,204 @@
// C++26 31.8.2.1 [stringstream.general]
// { dg-do run { target c++26 } }
#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.
#ifndef C
# define C char
# define L(a) a
#endif
using string = std::basic_string<C>;
using string_view = std::basic_string_view<C>;
using stringstream = std::basic_stringstream<C>;
struct convertible_to_string_view {
string s;
operator string_view() const { return s; }
};
const string str(L("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<C>>
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<string_view, stringstream>,
"stringstream(string_view, ios::openmode) is explicit");
static_assert(!std::is_convertible_v<const string_view, stringstream>,
"stringstream(string_view, ios::openmode) is explicit");
static_assert(!std::is_convertible_v<convertible_to_string_view, stringstream>,
"stringstream(convertible_to_string_view, ios::openmode) is explicit");
static_assert(!std::is_convertible_v<const convertible_to_string_view, stringstream>,
"stringstream(convertible_to_string_view, ios::openmode) is explicit");
{
stringstream strstr(cstr);
VERIFY( strstr.str() == cstr.s );
VERIFY( strstr.get() == cstr.s[0] );
}
{
stringstream strstr(ccstr);
VERIFY( strstr.str() == ccstr.s );
VERIFY( strstr.get() == ccstr.s[0] );
}
{
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 );
}
{
stringstream strstr(cstr, std::ios_base::out);
VERIFY( strstr.str() == cstr.s );
VERIFY( strstr.put('Y').good() );
VERIFY( strstr.get() == stringstream::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&)
stringstream::allocator_type a;
{
stringstream strstr(cstr, mode, a); // ={} checks for non-explicit ctor
VERIFY( strstr.str() == cstr.s );
}
{
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 );
}
{
stringstream strstr(cstr, std::ios::out, a);
VERIFY( strstr.str() == cstr.s );
VERIFY( strstr.put('X').good() );
VERIFY( strstr.get() == stringstream::traits_type::eof());
}
}
{
// template <typename T>
// basic_stringstream(const T&, ios_base::openmode)
{
stringstream strstr(cstr, mode);
VERIFY( strstr.str() == cstr.s );
VERIFY( strstr.get() == cstr.s[0] );
VERIFY( strstr.put('X').good() );
}
{
stringstream strstr(cstr, std::ios::in);
VERIFY( strstr.str() == cstr.s );
VERIFY( strstr.get() == cstr.s[0] );
VERIFY( strstr.put('X').rdstate() == strstr.badbit );
}
{
stringstream strstr(cstr, std::ios::out);
VERIFY( strstr.str() == cstr.s );
VERIFY( strstr.put('X').good() );
VERIFY( strstr.get() == stringstream::traits_type::eof());
}
}
{
// template <typename T>
// explicit
// basic_stringstream(const T&, ios_base::openmode = ios_base::in|ios_base::out)
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<C>;
template<typename Alloc, typename CC = typename Alloc::value_type>
using stringstream_with_alloc
= std::basic_stringstream<CC, std::char_traits<CC>, Alloc>;
void test03()
{
alloc_type a{1};
{
stringstream_with_alloc<alloc_type> strstr(cstr, a);
VERIFY( strstr.rdbuf()->get_allocator() == a );
VERIFY( 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( 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( string_view{strstr.str()} == cstr );
VERIFY( strstr.put('X').good() );
VERIFY( strstr.get() == stringstream::traits_type::eof());
}
}
void test04()
{
{
stringstream strstr;
strstr.str( cstr );
VERIFY( strstr.str() == cstr.s );
}
{
stringstream strstr;
strstr.str( ccstr );
VERIFY( strstr.str() == ccstr.s );
}
}
int
main()
{
test01();
test02();
test03();
test04();
}

View File

@@ -0,0 +1,6 @@
// C++26 [stringstream.general]
// { dg-do run { target c++26 } }
#define C wchar_t
#define L(a) L##a
#include "../char/string_view.cc"