[PATCH] libstdc++: implement P3044R2 - sub-string_view from string
Jonathan Wakely
jwakely@redhat.com
Thu Nov 20 18:24:15 GMT 2025
On Fri, 21 Nov 2025 at 01:50 +0800, Yuao Ma wrote:
>Hi all,
>
>This patch implements P3044R2 by adding a subview function for string
>and string_view. The implementation delegates directly to substr.
>Tested on x86_64-linux.
>Also, could you please help review my other patch regarding P3223R2 at
>https://gcc.gnu.org/pipermail/libstdc++/2025-November/064369.html?
>
>Thanks,
>Yuao
>From 6cf50b0e5a2fc1ca6814dbc4555c543c690d300a Mon Sep 17 00:00:00 2001
>From: Yuao Ma <c8ef@outlook.com>
>Date: Fri, 21 Nov 2025 01:42:51 +0800
>Subject: [PATCH] libstdc++: implement P3044R2 - sub-string_view from string
>
>libstdc++-v3/ChangeLog:
>
> * include/bits/basic_string.h: Add subview.
> * include/bits/version.def: Add string_subview FTM.
> * include/bits/version.h: Regenerate.
> * include/std/string: Add FTM.
> * include/std/string_view: Add subview.
> * testsuite/21_strings/basic_string/operations/subview/char.cc: New test.
> * testsuite/21_strings/basic_string/operations/subview/wchar_t.cc: New test.
> * testsuite/21_strings/basic_string_view/operations/subview/char.cc: New test.
> * testsuite/21_strings/basic_string_view/operations/subview/wchar_t.cc: New test.
>---
> libstdc++-v3/include/bits/basic_string.h | 19 +++++++
> libstdc++-v3/include/bits/version.def | 9 ++++
> libstdc++-v3/include/bits/version.h | 10 ++++
> libstdc++-v3/include/std/string | 1 +
> libstdc++-v3/include/std/string_view | 10 +++-
> .../basic_string/operations/subview/char.cc | 46 +++++++++++++++++
> .../operations/subview/wchar_t.cc | 46 +++++++++++++++++
> .../operations/subview/char.cc | 50 ++++++++++++++++++
> .../operations/subview/wchar_t.cc | 51 +++++++++++++++++++
> 9 files changed, 241 insertions(+), 1 deletion(-)
> create mode 100644 libstdc++-v3/testsuite/21_strings/basic_string/operations/subview/char.cc
> create mode 100644 libstdc++-v3/testsuite/21_strings/basic_string/operations/subview/wchar_t.cc
> create mode 100644 libstdc++-v3/testsuite/21_strings/basic_string_view/operations/subview/char.cc
> create mode 100644 libstdc++-v3/testsuite/21_strings/basic_string_view/operations/subview/wchar_t.cc
>
>diff --git a/libstdc++-v3/include/bits/basic_string.h b/libstdc++-v3/include/bits/basic_string.h
>index c4b6b1064a9..e87eb93e286 100644
>--- a/libstdc++-v3/include/bits/basic_string.h
>+++ b/libstdc++-v3/include/bits/basic_string.h
>@@ -3442,6 +3442,25 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
> { return basic_string(*this,
> _M_check(__pos, "basic_string::substr"), __n); }
>
>+#ifdef __glibcxx_string_subview // >= C++26
>+ /**
>+ * @brief Get a subview.
>+ * @param __pos Index of first character (default 0).
>+ * @param __n Number of characters in subview (default remainder).
>+ * @return The subview.
>+ * @throw std::out_of_range If __pos > size().
>+ *
>+ * Construct and return a subview using the @a __n characters starting
Also, we can use markdown formatting instead of Doxygen commands, so
`__pos` instead of @a __pos.
>+ * at @a __pos. If the string is too short, use the remainder of the
>+ * characters. If @a __pos is beyond the end of the string,
>+ * out_of_range is thrown.
>+ */
>+ _GLIBCXX_NODISCARD constexpr
>+ basic_string_view<_CharT, _Traits>
>+ subview(size_type __pos = 0, size_type __n = npos) const
>+ { return __sv_type(*this).subview(__pos, __n); }
>+#endif
>+
> /**
> * @brief Compare to a string.
> * @param __str String to compare against.
>diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
>index 29ecf15c7e3..b5575d2399f 100644
>--- a/libstdc++-v3/include/bits/version.def
>+++ b/libstdc++-v3/include/bits/version.def
>@@ -1934,6 +1934,15 @@ ftms = {
> };
> };
>
>+ftms = {
>+ name = string_subview;
>+ values = {
>+ v = 202506;
>+ cxxmin = 26;
>+ hosted = yes;
>+ };
>+};
>+
> ftms = {
> name = to_underlying;
> values = {
>diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
>index 5901d27113d..413da56b088 100644
>--- a/libstdc++-v3/include/bits/version.h
>+++ b/libstdc++-v3/include/bits/version.h
>@@ -2161,6 +2161,16 @@
> #endif /* !defined(__cpp_lib_string_resize_and_overwrite) */
> #undef __glibcxx_want_string_resize_and_overwrite
>
>+#if !defined(__cpp_lib_string_subview)
>+# if (__cplusplus > 202302L) && _GLIBCXX_HOSTED
>+# define __glibcxx_string_subview 202506L
>+# if defined(__glibcxx_want_all) || defined(__glibcxx_want_string_subview)
>+# define __cpp_lib_string_subview 202506L
>+# endif
>+# endif
>+#endif /* !defined(__cpp_lib_string_subview) */
>+#undef __glibcxx_want_string_subview
>+
> #if !defined(__cpp_lib_to_underlying)
> # if (__cplusplus >= 202100L)
> # define __glibcxx_to_underlying 202102L
>diff --git a/libstdc++-v3/include/std/string b/libstdc++-v3/include/std/string
>index 97ded057a87..918b4158b47 100644
>--- a/libstdc++-v3/include/std/string
>+++ b/libstdc++-v3/include/std/string
>@@ -63,6 +63,7 @@
> #define __glibcxx_want_erase_if
> #define __glibcxx_want_nonmember_container_access
> #define __glibcxx_want_string_resize_and_overwrite
>+#define __glibcxx_want_string_subview
> #define __glibcxx_want_string_udls
> #define __glibcxx_want_to_string
> #include <bits/version.h>
>diff --git a/libstdc++-v3/include/std/string_view b/libstdc++-v3/include/std/string_view
>index 842f6ad89af..b226544fa6f 100644
>--- a/libstdc++-v3/include/std/string_view
>+++ b/libstdc++-v3/include/std/string_view
>@@ -40,9 +40,10 @@
> #define __glibcxx_want_constexpr_char_traits
> #define __glibcxx_want_constexpr_string_view
> #define __glibcxx_want_freestanding_string_view
>-#define __glibcxx_want_string_view
> #define __glibcxx_want_starts_ends_with
> #define __glibcxx_want_string_contains
>+#define __glibcxx_want_string_subview
>+#define __glibcxx_want_string_view
> #include <bits/version.h>
>
> #if __cplusplus >= 201703L
>@@ -342,6 +343,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> return basic_string_view{_M_str + __pos, __rlen};
> }
>
>+#ifdef __glibcxx_string_subview // >= C++26
>+ [[nodiscard]]
>+ constexpr basic_string_view
>+ subview(size_type __pos = 0, size_type __n = npos) const
>+ { return substr(__pos, __n); }
>+#endif
>+
> [[nodiscard]]
> constexpr int
> compare(basic_string_view __str) const noexcept
>diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/operations/subview/char.cc b/libstdc++-v3/testsuite/21_strings/basic_string/operations/subview/char.cc
>new file mode 100644
>index 00000000000..c384948cc52
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/21_strings/basic_string/operations/subview/char.cc
>@@ -0,0 +1,46 @@
>+// { dg-do run { target c++26 } }
>+
>+#include <stdexcept>
>+#include <string>
>+#include <string_view>
>+#include <testsuite_hooks.h>
>+
>+void test01(void) {
>+ typedef std::string::size_type csize_type;
>+ typedef std::string::const_reference cref;
>+ typedef std::string::reference ref;
>+ csize_type csz01;
>+
>+ const char str_lit01[] = "rockaway, pacifica";
>+ const std::string str01(str_lit01);
>+ std::string_view str02;
>+
>+ csz01 = str01.size();
>+ str02 = str01.subview(0, 1);
>+ VERIFY(str02 == "r");
>+ str02 = str01.subview(10);
>+ VERIFY(str02 == "pacifica");
>+
>+ try {
>+ str02 = str01.subview(csz01 + 1);
>+ VERIFY(false);
>+ } catch (std::out_of_range &fail) {
>+ VERIFY(true);
>+ } catch (...) {
>+ VERIFY(false);
>+ }
>+
>+ try {
>+ str02 = str01.subview(csz01);
>+ VERIFY(str02.size() == 0);
>+ } catch (std::out_of_range &fail) {
>+ VERIFY(false);
>+ } catch (...) {
>+ VERIFY(false);
>+ }
>+}
>+
>+int main() {
>+ test01();
>+ return 0;
>+}
>diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/operations/subview/wchar_t.cc b/libstdc++-v3/testsuite/21_strings/basic_string/operations/subview/wchar_t.cc
>new file mode 100644
>index 00000000000..3b8e6a87fe6
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/21_strings/basic_string/operations/subview/wchar_t.cc
>@@ -0,0 +1,46 @@
>+// { dg-do run { target c++26 } }
>+
>+#include <stdexcept>
>+#include <string>
>+#include <string_view>
>+#include <testsuite_hooks.h>
>+
>+void test01(void) {
>+ typedef std::wstring::size_type csize_type;
>+ typedef std::wstring::const_reference cref;
>+ typedef std::wstring::reference ref;
>+ csize_type csz01;
>+
>+ const wchar_t str_lit01[] = L"rockaway, pacifica";
>+ const std::wstring str01(str_lit01);
>+ std::wstring_view str02;
>+
>+ csz01 = str01.size();
>+ str02 = str01.subview(0, 1);
>+ VERIFY(str02 == L"r");
>+ str02 = str01.subview(10);
>+ VERIFY(str02 == L"pacifica");
>+
>+ try {
>+ str02 = str01.subview(csz01 + 1);
>+ VERIFY(false);
>+ } catch (std::out_of_range &fail) {
>+ VERIFY(true);
>+ } catch (...) {
>+ VERIFY(false);
>+ }
>+
>+ try {
>+ str02 = str01.subview(csz01);
>+ VERIFY(str02.size() == 0);
>+ } catch (std::out_of_range &fail) {
>+ VERIFY(false);
>+ } catch (...) {
>+ VERIFY(false);
>+ }
>+}
>+
>+int main() {
>+ test01();
>+ return 0;
>+}
>diff --git a/libstdc++-v3/testsuite/21_strings/basic_string_view/operations/subview/char.cc b/libstdc++-v3/testsuite/21_strings/basic_string_view/operations/subview/char.cc
>new file mode 100644
>index 00000000000..f0ce145b45b
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/21_strings/basic_string_view/operations/subview/char.cc
>@@ -0,0 +1,50 @@
>+// { dg-do run { target c++26 } }
>+#include <string_view>
>+#include <testsuite_hooks.h>
>+
>+#if __STDC_HOSTED__
>+#include <stdexcept>
>+#endif
>+
>+void test01() {
>+ typedef std::string_view::size_type csize_type;
>+ typedef std::string_view::const_reference cref;
>+ typedef std::string_view::reference ref;
>+ csize_type csz01;
>+
>+ const char str_lit01[] = "rockaway, pacifica";
>+ const std::string_view str01(str_lit01);
>+ std::string_view str02;
>+
>+ csz01 = str01.size();
>+ str02 = str01.subview(0, 1);
>+ VERIFY(str02 == "r");
>+ str02 = str01.subview(10);
>+ VERIFY(str02 == "pacifica");
>+
>+#if __STDC_HOSTED__
>+ try {
>+ str02 = str01.subview(csz01 + 1);
>+ VERIFY(false);
>+ } catch (std::out_of_range &fail) {
>+ VERIFY(true);
>+ } catch (...) {
>+ VERIFY(false);
>+ }
>+
>+ try {
>+ str02 = str01.subview(csz01);
>+ VERIFY(str02.size() == 0);
>+ VERIFY(str02.begin() == str01.end());
>+ VERIFY(true);
>+ } catch (...) {
>+ VERIFY(false);
>+ }
>+#endif // HOSTED
>+}
>+
>+int main() {
>+ test01();
>+
>+ return 0;
>+}
>diff --git a/libstdc++-v3/testsuite/21_strings/basic_string_view/operations/subview/wchar_t.cc b/libstdc++-v3/testsuite/21_strings/basic_string_view/operations/subview/wchar_t.cc
>new file mode 100644
>index 00000000000..86b50959b5c
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/21_strings/basic_string_view/operations/subview/wchar_t.cc
>@@ -0,0 +1,51 @@
>+// { dg-do run { target c++26 } }
>+
>+#include <string_view>
>+#include <testsuite_hooks.h>
>+
>+#if __STDC_HOSTED__
>+#include <stdexcept>
>+#endif
>+
>+void test01() {
>+ typedef std::wstring_view::size_type csize_type;
>+ typedef std::wstring_view::const_reference cref;
>+ typedef std::wstring_view::reference ref;
>+ csize_type csz01;
>+
>+ const wchar_t str_lit01[] = L"rockaway, pacifica";
>+ const std::wstring_view str01(str_lit01);
>+ std::wstring_view str02;
>+
>+ csz01 = str01.size();
>+ str02 = str01.subview(0, 1);
>+ VERIFY(str02 == L"r");
>+ str02 = str01.subview(10);
>+ VERIFY(str02 == L"pacifica");
>+
>+#if __STDC_HOSTED__
>+ try {
>+ str02 = str01.subview(csz01 + 1);
>+ VERIFY(false);
>+ } catch (std::out_of_range &fail) {
>+ VERIFY(true);
>+ } catch (...) {
>+ VERIFY(false);
>+ }
>+
>+ try {
>+ str02 = str01.subview(csz01);
>+ VERIFY(str02.size() == 0);
>+ VERIFY(str02.begin() == str01.end());
>+ VERIFY(true);
>+ } catch (...) {
>+ VERIFY(false);
>+ }
>+#endif // HOSTED
>+}
>+
>+int main() {
>+ test01();
>+
>+ return 0;
>+}
>--
>2.51.1
>
More information about the Libstdc++
mailing list