[committed] libstdc++: Define std::basic_string::resize_and_overwrite for C++23 (P1072R10)

Message ID YWoTU1NNEpMTJpgo@redhat.com
State Committed
Commit 929abc7fe3ad4491ac412ca232e055618559f268
Headers
Series [committed] libstdc++: Define std::basic_string::resize_and_overwrite for C++23 (P1072R10) |

Commit Message

Jonathan Wakely Oct. 15, 2021, 11:48 p.m. UTC
  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.

Tested powerpc64le-linux. Committed to trunk.
commit 929abc7fe3ad4491ac412ca232e055618559f268
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Fri Oct 15 22:01:25 2021

    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.
  

Patch

diff --git a/libstdc++-v3/include/bits/basic_string.h b/libstdc++-v3/include/bits/basic_string.h
index 59c84b1b6ad..a6575fa9e26 100644
--- a/libstdc++-v3/include/bits/basic_string.h
+++ b/libstdc++-v3/include/bits/basic_string.h
@@ -971,6 +971,13 @@  _GLIBCXX_BEGIN_NAMESPACE_CXX11
 #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 371f1c3ccee..98c386239f9 100644
--- a/libstdc++-v3/include/bits/basic_string.tcc
+++ b/libstdc++-v3/include/bits/basic_string.tcc
@@ -515,6 +515,37 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       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 0d7ae3bf857..2b118301da7 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 00000000000..f0e81126a41
--- /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();
+}