libstdc++: Define std::basic_string::resize_and_overwrite for C++23 (P1072R10)
A recently approved change for the C++23 working draft.
libstdc++-v3/ChangeLog:
* include/bits/basic_string.h (__cpp_lib_string_resize_and_overwrite):
Define for C++23.
(basic_string::resize_and_overwrite): Declare.
* include/bits/basic_string.tcc (basic_string::resize_and_overwrite):
Define.
* include/std/version (__cpp_lib_resize_and_overwrite): Define
for C++23.
* testsuite/21_strings/basic_string/capacity/char/resize_and_overwrite.cc:
New test.
diff --git a/libstdc++-v3/include/bits/basic_string.h b/libstdc++-v3/include/bits/basic_string.h
index 59c84b1..a6575fa 100644
--- a/libstdc++-v3/include/bits/basic_string.h
+++ b/libstdc++-v3/include/bits/basic_string.h
@@ -971,6 +971,13 @@
#pragma GCC diagnostic pop
#endif
+#if __cplusplus > 202002L
+#define __cpp_lib_string_resize_and_overwrite 202110L
+ template<typename _Operation>
+ constexpr void
+ resize_and_overwrite(size_type __n, _Operation __op);
+#endif
+
/**
* Returns the total number of characters that the %string can hold
* before needing to allocate more memory.
diff --git a/libstdc++-v3/include/bits/basic_string.tcc b/libstdc++-v3/include/bits/basic_string.tcc
index 371f1c3..98c3862 100644
--- a/libstdc++-v3/include/bits/basic_string.tcc
+++ b/libstdc++-v3/include/bits/basic_string.tcc
@@ -515,6 +515,37 @@
return __n;
}
+#if __cplusplus > 202002L
+ template<typename _CharT, typename _Traits, typename _Alloc>
+ template<typename _Operation>
+ constexpr void
+ basic_string<_CharT, _Traits, _Alloc>::
+ resize_and_overwrite(size_type __n, _Operation __op)
+ {
+ const size_type __capacity = capacity();
+ _CharT* __p;
+ if (__n > __capacity)
+ {
+ __p = _M_create(__n, __capacity);
+ this->_S_copy(__p, _M_data(), length()); // exclude trailing null
+ _M_dispose();
+ _M_data(__p);
+ _M_capacity(__n);
+ }
+ else
+ __p = _M_data();
+ struct _Terminator {
+ ~_Terminator() { _M_this->_M_set_length(_M_r); }
+ basic_string* _M_this;
+ size_type _M_r;
+ };
+ _Terminator __term{this};
+ const size_type __n2 [[maybe_unused]] = __n;
+ __term._M_r = std::move(__op)(__p, __n);
+ _GLIBCXX_DEBUG_ASSERT(__term._M_r >= 0 && __term._M_r <= __n2);
+ }
+#endif // C++23
+
#endif // _GLIBCXX_USE_CXX11_ABI
template<typename _CharT, typename _Traits, typename _Alloc>
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index 0d7ae3b..2b11830 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -294,6 +294,9 @@
#define __cpp_lib_is_scoped_enum 202011L
#define __cpp_lib_move_only_function 202110L
#define __cpp_lib_string_contains 202011L
+#if _GLIBCXX_USE_CXX11_ABI // Only supported with cxx11-abi
+# define __cpp_lib_string_resize_and_overwrite 202110L
+#endif
#define __cpp_lib_to_underlying 202102L
#endif // C++2b
#endif // C++20
diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/char/resize_and_overwrite.cc b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/char/resize_and_overwrite.cc
new file mode 100644
index 0000000..f0e8112
--- /dev/null
+++ b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/char/resize_and_overwrite.cc
@@ -0,0 +1,114 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target { c++23 && cxx11-abi } } }
+
+#include <string>
+
+#ifndef __cpp_lib_string_resize_and_overwrite
+#error "Feature test macro for resize_and_overwrite is missing in <string>"
+#elif __cpp_lib_string_resize_and_overwrite != 202110L
+# error "Feature test macro for resize_and_overwrite has wrong value in <string>"
+#endif
+
+
+#include <cstring>
+#include <testsuite_hooks.h>
+
+// P1072R10 basic_string::resize_and_overwrite
+
+void
+test01()
+{
+ std::string s = "foo";
+ s.resize_and_overwrite(99, [](char* p, int n) {
+ VERIFY( n == 99 );
+ VERIFY( !std::strncmp(p, "foo", 3) );
+ std::strcpy(p, "monkey tennis");
+ return 6;
+ });
+ VERIFY( s == "monkey" );
+ VERIFY( s.size() == 6 );
+ VERIFY( s.capacity() >= 99 );
+ VERIFY( s[6] == '\0' );
+
+ const auto str = s.data();
+
+ s.resize_and_overwrite(50, [](char* p, int n) -> unsigned {
+ VERIFY( n == 50 );
+ VERIFY( !std::strncmp(p, "monkey", 3) );
+ std::strcpy(p, "Partridge among the pidgeons");
+ return 9;
+ });
+ VERIFY( s.data() == str ); // No reallocation
+ VERIFY( s == "Partridge" );
+ VERIFY( s[9] == '\0' );
+}
+
+void
+test02()
+{
+ std::string s;
+ auto p = s.data();
+ s.resize_and_overwrite(0, [](auto...) { return 0; });
+ VERIFY( s.empty() );
+ VERIFY( s[0] == '\0' );
+ VERIFY( s.data() == p );
+
+ s = "short string";
+ p = s.data();
+ s.resize_and_overwrite(0, [](auto...) { return 0; });
+ VERIFY( s.empty() );
+ VERIFY( s[0] == '\0' );
+ VERIFY( s.data() == p );
+
+ s = "a string that is long enough to not be a short string";
+ p = s.data();
+ s.resize_and_overwrite(0, [](auto...) { return 0; });
+ VERIFY( s.empty() );
+ VERIFY( s[0] == '\0' );
+ VERIFY( s.data() == p );
+}
+
+void
+test03()
+{
+ struct Op
+ {
+ int operator()(char*, int) & = delete;
+ int operator()(char*, int) const & = delete;
+ int operator()(char* p, int n) && { std::memset(p, 'a', n+1); return n; }
+ int operator()(char*, int) const && = delete;
+ };
+ std::string s;
+ s.resize_and_overwrite(42, Op{});
+ VERIFY( s.size() == 42 );
+ VERIFY( s == std::string(42, 'a') );
+ VERIFY( s[42] == '\0' );
+
+ s.resize_and_overwrite(0, [](auto&& p, auto&& n) {
+ static_assert( std::is_same_v<decltype(p), char*&> );
+ static_assert( std::is_same_v<decltype(n), std::string::size_type&> );
+ return 0;
+ });
+}
+
+void
+test04()
+{
+ std::string s = "this tests how the library copes with undefined behaviour";
+
+ try {
+ s.resize_and_overwrite(13, [](auto...) -> int { throw "undefined"; });
+ } catch (...) {
+ // The standard doesn't require this, but we leave the string empty:
+ VERIFY( s.size() == 0 );
+ VERIFY( s[0] == '\0' );
+ }
+}
+
+int main()
+{
+ test01();
+ test02();
+ test03();
+ test04();
+}