[libcxx-commits] [libcxx] WIP [libc++][string] P3044R2: sub-`string_view` from `string` (PR #147095)
via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Jul 4 17:17:20 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx
Author: Hristo Hristov (H-G-Hristov)
<details>
<summary>Changes</summary>
Implements [P3044R2](https://wg21.link/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
- https://github.com/cplusplus/draft/pull/7975
---
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
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;
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/147095
More information about the libcxx-commits
mailing list