Skip to content

WIP [libc++][string] P3044R2: sub-string_view from string #147095

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

H-G-Hristov
Copy link
Contributor

@H-G-Hristov H-G-Hristov commented Jul 4, 2025

Implements P3044R2

Note: substr.pass.cpp is refactored to accommodate the test of basic_string_view's subview which is an alias of substr without changing the test cases.

References

@Zingam Zingam added libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. c++26 labels Jul 5, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 5, 2025

@llvm/pr-subscribers-libcxx

Author: Hristo Hristov (H-G-Hristov)

Changes

Implements P3044R2

Note: substr.pass.cpp is refactored to accommodate the test of basic_string_view's subview which is an alias of substr without changing the test cases.

References


Full diff: https://github.com/llvm/llvm-project/pull/147095.diff

5 Files Affected:

  • (modified) libcxx/include/string (+9)
  • (modified) libcxx/include/string_view (+8)
  • (added) libcxx/test/std/strings/basic.string/string.ops/string_substr/subview.pass.cpp (+33)
  • (modified) libcxx/test/std/strings/string.view/string.view.ops/substr.pass.cpp (+92-60)
  • (added) libcxx/test/std/strings/string.view/string.view.ops/subview.pass.cpp (+20)
diff --git a/libcxx/include/string b/libcxx/include/string
index c450ddcb41914..9476c3df3b8e7 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -280,6 +280,8 @@ public:
     basic_string substr(size_type pos = 0, size_type n = npos) const;                           // constexpr in C++20, removed in C++23
     basic_string substr(size_type pos = 0, size_type n = npos) const&;                          // since C++23
     constexpr basic_string substr(size_type pos = 0, size_type n = npos) &&;                    // since C++23
+    constexpr basic_string_view<charT, traits> subview(size_type pos = 0,
+                                                       size_type n = npos) const;               // since C++26
     void swap(basic_string& str)
         noexcept(allocator_traits<allocator_type>::propagate_on_container_swap::value ||
                  allocator_traits<allocator_type>::is_always_equal::value);                     // C++17, constexpr since C++20
@@ -1745,6 +1747,13 @@ public:
   }
 #  endif
 
+#  if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_string_view<_CharT, _Traits>
+  subview(size_type __pos = 0, size_type __n = npos) const {
+    return basic_string_view<_CharT, _Traits>(*this).subview(__pos, __n);
+  }
+#  endif
+
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(basic_string& __str)
 #  if _LIBCPP_STD_VER >= 14
       _NOEXCEPT;
diff --git a/libcxx/include/string_view b/libcxx/include/string_view
index 861187c0640e1..ad036882e1630 100644
--- a/libcxx/include/string_view
+++ b/libcxx/include/string_view
@@ -130,6 +130,8 @@ namespace std {
       size_type copy(charT* s, size_type n, size_type pos = 0) const;  // constexpr in C++20
 
       constexpr basic_string_view substr(size_type pos = 0, size_type n = npos) const;
+      constexpr basic_string_view subview(size_type pos = 0,
+                                          size_type n = npos) const;      // freestanding-deleted, since C++26
       constexpr int compare(basic_string_view s) const noexcept;
       constexpr int compare(size_type pos1, size_type n1, basic_string_view s) const;
       constexpr int compare(size_type pos1, size_type n1,
@@ -465,6 +467,12 @@ public:
                           : basic_string_view(__assume_valid(), data() + __pos, std::min(__n, size() - __pos));
   }
 
+#  if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_string_view subview(size_type __pos = 0, size_type __n = npos) const {
+    return this->substr(__pos, __n);
+  }
+#  endif
+
   _LIBCPP_CONSTEXPR_SINCE_CXX14 int compare(basic_string_view __sv) const _NOEXCEPT {
     size_type __rlen = std::min(size(), __sv.size());
     int __retval     = _Traits::compare(data(), __sv.data(), __rlen);
diff --git a/libcxx/test/std/strings/basic.string/string.ops/string_substr/subview.pass.cpp b/libcxx/test/std/strings/basic.string/string.ops/string_substr/subview.pass.cpp
new file mode 100644
index 0000000000000..65418dcb4d7be
--- /dev/null
+++ b/libcxx/test/std/strings/basic.string/string.ops/string_substr/subview.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <string>
+
+// constexpr basic_string_view<_CharT, _Traits> subview(size_type __pos = 0, size_type __n = npos) const;
+
+#include <cassert>
+#include <string>
+
+constexpr bool test() {
+  std::string s{"Hello cruel world!"};
+  auto sub = s.subview(6);
+  assert(sub == "cruel world!");
+  auto subsub = sub.subview(0, 5);
+  assert(subsub == "cruel");
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/strings/string.view/string.view.ops/substr.pass.cpp b/libcxx/test/std/strings/string.view/string.view.ops/substr.pass.cpp
index 62b0259c175f8..02755985ead70 100644
--- a/libcxx/test/std/strings/string.view/string.view.ops/substr.pass.cpp
+++ b/libcxx/test/std/strings/string.view/string.view.ops/substr.pass.cpp
@@ -11,6 +11,10 @@
 // <string_view>
 
 // constexpr basic_string_view substr(size_type pos = 0, size_type n = npos) const;
+// constexpr basic_string_view subview(size_type pos = 0,
+//                                     size_type n = npos) const;      // freestanding-deleted
+
+// subview is alternative name of substr
 
 // Throws: out_of_range if pos > size().
 // Effects: Determines the effective length rlen of the string to reference as the smaller of n and size() - pos.
@@ -25,15 +29,21 @@
 #include "test_macros.h"
 
 template <typename CharT>
-void test1(std::basic_string_view<CharT> sv, std::size_t n, size_t pos) {
+struct Test {
+  typedef std::basic_string_view<CharT> (std::basic_string_view<CharT>::*Sub)(
+      typename std::basic_string_view<CharT>::size_type, typename std::basic_string_view<CharT>::size_type) const;
+};
+
+template <typename CharT, typename Test<CharT>::Sub TestSub>
+void testDetail(std::basic_string_view<CharT> sv, std::size_t n, size_t pos) {
   std::basic_string_view<CharT> sv1;
 #ifdef TEST_HAS_NO_EXCEPTIONS
   if (pos > sv.size())
     return; // would throw if exceptions were enabled
-  sv1 = sv.substr(pos, n);
+  sv1 = (sv.*TestSub)(pos, n);
 #else
   try {
-    sv1 = sv.substr(pos, n);
+    sv1 = (sv.*TestSub)(pos, n);
     assert(pos <= sv.size());
   } catch (const std::out_of_range&) {
     assert(pos > sv.size());
@@ -46,79 +56,101 @@ void test1(std::basic_string_view<CharT> sv, std::size_t n, size_t pos) {
     assert(sv[pos + i] == sv1[i]);
 }
 
-template <typename CharT>
-void test(const CharT* s) {
-  typedef std::basic_string_view<CharT> string_view_t;
+template <typename CharT, typename Test<CharT>::Sub TestSub>
+void testCases(const CharT* s) {
+  std::basic_string_view<CharT> sv(s);
 
-  string_view_t sv1(s);
+  testDetail<CharT, TestSub>(sv, 0, 0);
+  testDetail<CharT, TestSub>(sv, 1, 0);
+  testDetail<CharT, TestSub>(sv, 20, 0);
+  testDetail<CharT, TestSub>(sv, sv.size(), 0);
 
-  test1(sv1, 0, 0);
-  test1(sv1, 1, 0);
-  test1(sv1, 20, 0);
-  test1(sv1, sv1.size(), 0);
+  testDetail<CharT, TestSub>(sv, 100, 3);
 
-  test1(sv1, 0, 3);
-  test1(sv1, 2, 3);
-  test1(sv1, 100, 3);
+  testDetail<CharT, TestSub>(sv, 0, std::basic_string_view<CharT>::npos);
+  testDetail<CharT, TestSub>(sv, 2, std::basic_string_view<CharT>::npos);
+  testDetail<CharT, TestSub>(sv, sv.size(), std::basic_string_view<CharT>::npos);
 
-  test1(sv1, 0, string_view_t::npos);
-  test1(sv1, 2, string_view_t::npos);
-  test1(sv1, sv1.size(), string_view_t::npos);
+  testDetail<CharT, TestSub>(sv, sv.size() + 1, 0);
+  testDetail<CharT, TestSub>(sv, sv.size() + 1, 1);
+  testDetail<CharT, TestSub>(sv, sv.size() + 1, std::basic_string_view<CharT>::npos);
+}
 
-  test1(sv1, sv1.size() + 1, 0);
-  test1(sv1, sv1.size() + 1, 1);
-  test1(sv1, sv1.size() + 1, string_view_t::npos);
+template <typename CharT>
+void testSubs(const CharT* s) {
+  testCases<CharT, &std::basic_string_view<CharT>::substr>(s);
+#if TEST_STD_VER >= 26
+  testCases<CharT, &std::basic_string_view<CharT>::subview>(s);
+#endif // TEST_STD_VER >= 26
 }
 
-int main(int, char**) {
-  test("ABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDE");
-  test("ABCDE");
-  test("a");
-  test("");
+void test() {
+  testSubs("ABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDE");
+  testSubs("ABCDE");
+  testSubs("a");
+  testSubs("");
 
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test(L"ABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDE");
-  test(L"ABCDE");
-  test(L"a");
-  test(L"");
+  testSubs(
+      L"ABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDE");
+  testSubs(L"ABCDE");
+  testSubs(L"a");
+  testSubs(L"");
 #endif
 
 #if TEST_STD_VER >= 11
-  test(u"ABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDE");
-  test(u"ABCDE");
-  test(u"a");
-  test(u"");
-
-  test(U"ABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDE");
-  test(U"ABCDE");
-  test(U"a");
-  test(U"");
+  testSubs(
+      u"ABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDE");
+  testSubs(u"ABCDE");
+  testSubs(u"a");
+  testSubs(u"");
+
+  testSubs(
+      U"ABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDE");
+  testSubs(U"ABCDE");
+  testSubs(U"a");
+  testSubs(U"");
 #endif
+}
+
+#if TEST_STD_VER >= 14
+template <typename Test<char>::Sub TestSub>
+constexpr void testConstexprDetail() {
+  constexpr std::string_view sv{"ABCDE", 5};
+  {
+    constexpr std::string_view sv2 = (sv.*TestSub)(0, 3);
+
+    static_assert(sv2.size() == 3, "");
+    static_assert(sv2[0] == 'A', "");
+    static_assert(sv2[1] == 'B', "");
+    static_assert(sv2[2] == 'C', "");
+  }
 
-#if TEST_STD_VER > 11
   {
-    constexpr std::string_view sv1{"ABCDE", 5};
-
-    {
-      constexpr std::string_view sv2 = sv1.substr(0, 3);
-      static_assert(sv2.size() == 3, "");
-      static_assert(sv2[0] == 'A', "");
-      static_assert(sv2[1] == 'B', "");
-      static_assert(sv2[2] == 'C', "");
-    }
-
-    {
-      constexpr std::string_view sv2 = sv1.substr(3, 0);
-      static_assert(sv2.size() == 0, "");
-    }
-
-    {
-      constexpr std::string_view sv2 = sv1.substr(3, 3);
-      static_assert(sv2.size() == 2, "");
-      static_assert(sv2[0] == 'D', "");
-      static_assert(sv2[1] == 'E', "");
-    }
+    constexpr std::string_view sv2 = (sv.*TestSub)(3, 0);
+    static_assert(sv2.size() == 0, "");
   }
+
+  {
+    constexpr std::string_view sv2 = (sv.*TestSub)(3, 3);
+    static_assert(sv2.size() == 2, "");
+    static_assert(sv2[0] == 'D', "");
+    static_assert(sv2[1] == 'E', "");
+  }
+}
+
+void test_constexpr() {
+  testConstexprDetail<&std::string_view::substr>();
+#  if TEST_STD_VER >= 26
+  testConstexprDetail<&std::string_view::subview>();
+#  endif
+}
+#endif
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 14
+  test_constexpr();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/strings/string.view/string.view.ops/subview.pass.cpp b/libcxx/test/std/strings/string.view/string.view.ops/subview.pass.cpp
new file mode 100644
index 0000000000000..fec258eef4546
--- /dev/null
+++ b/libcxx/test/std/strings/string.view/string.view.ops/subview.pass.cpp
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <string_view>
+
+// constexpr basic_string_view subview(size_type pos = 0,
+//                                     size_type n = npos) const;      // freestanding-deleted
+
+int main(int, char**) {
+  // This test is intentionally empty because subview is an alias for substr
+  // and is tested in substr.pass.cpp.
+  return 0;
+}

Comment on lines +17 to +18
// This test is intentionally empty because subview is an alias for substr
// and is tested in substr.pass.cpp.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we don't need this file.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what the current practice about function aliases is. Normally we want a test file per function. This one is to tell the user what to look at. Are there any similar cases in the library?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++26 libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants