diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index a36848ebd24b4..0008232ae91d5 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -506,6 +506,8 @@ Status ---------------------------------------------------------- ----------------- ``__cpp_lib_sstream_from_string_view`` ``202306L`` ---------------------------------------------------------- ----------------- + ``__cpp_lib_string_subview`` ``202506L`` + ---------------------------------------------------------- ----------------- ``__cpp_lib_string_view`` ``202403L`` ---------------------------------------------------------- ----------------- ``__cpp_lib_submdspan`` *unimplemented* diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst index 8b8dce5083149..86ec7dc14bb48 100644 --- a/libcxx/docs/ReleaseNotes/22.rst +++ b/libcxx/docs/ReleaseNotes/22.rst @@ -39,6 +39,7 @@ Implemented Papers ------------------ - P2321R2: ``zip`` (`Github `__) (The paper is partially implemented. ``zip_transform_view`` is implemented in this release) +- P3044R2: sub-``string_view`` from ``string`` (`Github `__) Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index e8b0c9559f40b..44bbc257fd501 100644 --- a/libcxx/docs/Status/Cxx2cPapers.csv +++ b/libcxx/docs/Status/Cxx2cPapers.csv @@ -129,7 +129,7 @@ "`P3179R9 `__","Parallel Range Algorithms","2025-06 (Sofia)","","","" "`P3709R2 `__","Reconsider parallel ``ranges::rotate_copy`` and ``ranges::reverse_copy``","2025-06 (Sofia)","","","" "`P3641R0 `__","Rename ``std::observable`` to ``std::observable_checkpoint``, and add a feature-test macro","2025-06 (Sofia)","","","" -"`P3044R2 `__","sub-``string_view`` from ``string``","2025-06 (Sofia)","","","" +"`P3044R2 `__","sub-``string_view`` from ``string``","2025-06 (Sofia)","|Complete|","22","" "`P2876R3 `__","Proposal to extend ``std::simd`` with more constructors and accessors","2025-06 (Sofia)","","","" "`P3480R6 `__","``std::simd`` is a range","2025-06 (Sofia)","","","" "`P2664R11 `__","Extend ``std::simd`` with permutation API","2025-06 (Sofia)","","","" diff --git a/libcxx/include/string b/libcxx/include/string index 98297d04d0c61..7e8d8fac9f753 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 subview(size_type pos = 0, + size_type n = npos) const; // since C++26 void swap(basic_string& str) noexcept(allocator_traits::propagate_on_container_swap::value || allocator_traits::is_always_equal::value); // C++17, constexpr since C++20 @@ -1752,6 +1754,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 f86b2722aca6c..e38a7ce7c6a73 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, @@ -457,14 +459,25 @@ public: return __rlen; } - _LIBCPP_CONSTEXPR _LIBCPP_HIDE_FROM_ABI basic_string_view substr(size_type __pos = 0, size_type __n = npos) const { + _LIBCPP_CONSTEXPR _LIBCPP_HIDE_FROM_ABI inline basic_string_view + __subview(size_type __pos, size_type __n, const char* __msg) const { // Use the `__assume_valid` form of the constructor to avoid an unnecessary check. Any substring of a view is a // valid view. In particular, `size()` is known to be smaller than `numeric_limits::max()`, so the // new size is also smaller. See also https://github.com/llvm/llvm-project/issues/91634. - return __pos > size() ? (__throw_out_of_range("string_view::substr"), basic_string_view()) + return __pos > size() ? (__throw_out_of_range(__msg), basic_string_view()) : basic_string_view(__assume_valid(), data() + __pos, std::min(__n, size() - __pos)); } + _LIBCPP_CONSTEXPR _LIBCPP_HIDE_FROM_ABI basic_string_view substr(size_type __pos = 0, size_type __n = npos) const { + return __subview(__pos, __n, "string_view::substr"); + } + +# if _LIBCPP_STD_VER >= 26 + _LIBCPP_HIDE_FROM_ABI constexpr basic_string_view subview(size_type __pos = 0, size_type __n = npos) const { + return __subview(__pos, __n, "string_view::subview"); + } +# 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/include/version b/libcxx/include/version index aae9277a7dfc6..f2fb9aef923ee 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -245,6 +245,7 @@ __cpp_lib_starts_ends_with 201711L __cpp_lib_string_contains 202011L __cpp_lib_string_resize_and_overwrite 202110L +__cpp_lib_string_subview 202506L __cpp_lib_string_udls 201304L __cpp_lib_string_view 202403L 201803L // C++20 @@ -599,6 +600,7 @@ __cpp_lib_void_t 201411L # define __cpp_lib_span_at 202311L # define __cpp_lib_span_initializer_list 202311L # define __cpp_lib_sstream_from_string_view 202306L +# define __cpp_lib_string_subview 202506L # undef __cpp_lib_string_view # define __cpp_lib_string_view 202403L // # define __cpp_lib_submdspan 202306L diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp index 7236d5d7f2aca..147854eead2cc 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp @@ -60,6 +60,10 @@ # error "__cpp_lib_string_resize_and_overwrite should not be defined before c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifdef __cpp_lib_string_udls # error "__cpp_lib_string_udls should not be defined before c++14" # endif @@ -114,6 +118,10 @@ # error "__cpp_lib_string_resize_and_overwrite should not be defined before c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifndef __cpp_lib_string_udls # error "__cpp_lib_string_udls should be defined in c++14" # endif @@ -177,6 +185,10 @@ # error "__cpp_lib_string_resize_and_overwrite should not be defined before c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifndef __cpp_lib_string_udls # error "__cpp_lib_string_udls should be defined in c++17" # endif @@ -261,6 +273,10 @@ # error "__cpp_lib_string_resize_and_overwrite should not be defined before c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifndef __cpp_lib_string_udls # error "__cpp_lib_string_udls should be defined in c++20" # endif @@ -354,6 +370,10 @@ # error "__cpp_lib_string_resize_and_overwrite should have the value 202110L in c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifndef __cpp_lib_string_udls # error "__cpp_lib_string_udls should be defined in c++23" # endif @@ -456,6 +476,13 @@ # error "__cpp_lib_string_resize_and_overwrite should have the value 202110L in c++26" # endif +# ifndef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should be defined in c++26" +# endif +# if __cpp_lib_string_subview != 202506L +# error "__cpp_lib_string_subview should have the value 202506L in c++26" +# endif + # ifndef __cpp_lib_string_udls # error "__cpp_lib_string_udls should be defined in c++26" # endif diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/string_view.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/string_view.version.compile.pass.cpp index c7bafb0bf059c..2c3716111102a 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/string_view.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/string_view.version.compile.pass.cpp @@ -40,6 +40,10 @@ # error "__cpp_lib_string_contains should not be defined before c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifdef __cpp_lib_string_view # error "__cpp_lib_string_view should not be defined before c++17" # endif @@ -66,6 +70,10 @@ # error "__cpp_lib_string_contains should not be defined before c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifdef __cpp_lib_string_view # error "__cpp_lib_string_view should not be defined before c++17" # endif @@ -92,6 +100,10 @@ # error "__cpp_lib_string_contains should not be defined before c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifndef __cpp_lib_string_view # error "__cpp_lib_string_view should be defined in c++17" # endif @@ -136,6 +148,10 @@ # error "__cpp_lib_string_contains should not be defined before c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifndef __cpp_lib_string_view # error "__cpp_lib_string_view should be defined in c++20" # endif @@ -183,6 +199,10 @@ # error "__cpp_lib_string_contains should have the value 202011L in c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifndef __cpp_lib_string_view # error "__cpp_lib_string_view should be defined in c++23" # endif @@ -239,6 +259,13 @@ # error "__cpp_lib_string_contains should have the value 202011L in c++26" # endif +# ifndef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should be defined in c++26" +# endif +# if __cpp_lib_string_subview != 202506L +# error "__cpp_lib_string_subview should have the value 202506L in c++26" +# endif + # ifndef __cpp_lib_string_view # error "__cpp_lib_string_view should be defined in c++26" # endif diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp index 7bd8e8979e6f3..3c21ed92cfc49 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp @@ -820,6 +820,10 @@ # error "__cpp_lib_string_resize_and_overwrite should not be defined before c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifdef __cpp_lib_string_udls # error "__cpp_lib_string_udls should not be defined before c++14" # endif @@ -1775,6 +1779,10 @@ # error "__cpp_lib_string_resize_and_overwrite should not be defined before c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifndef __cpp_lib_string_udls # error "__cpp_lib_string_udls should be defined in c++14" # endif @@ -2916,6 +2924,10 @@ # error "__cpp_lib_string_resize_and_overwrite should not be defined before c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifndef __cpp_lib_string_udls # error "__cpp_lib_string_udls should be defined in c++17" # endif @@ -4330,6 +4342,10 @@ # error "__cpp_lib_string_resize_and_overwrite should not be defined before c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifndef __cpp_lib_string_udls # error "__cpp_lib_string_udls should be defined in c++20" # endif @@ -5978,6 +5994,10 @@ # error "__cpp_lib_string_resize_and_overwrite should have the value 202110L in c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifndef __cpp_lib_string_udls # error "__cpp_lib_string_udls should be defined in c++23" # endif @@ -7941,6 +7961,13 @@ # error "__cpp_lib_string_resize_and_overwrite should have the value 202110L in c++26" # endif +# ifndef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should be defined in c++26" +# endif +# if __cpp_lib_string_subview != 202506L +# error "__cpp_lib_string_subview should have the value 202506L in c++26" +# endif + # ifndef __cpp_lib_string_udls # error "__cpp_lib_string_udls should be defined in c++26" # endif 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..7b9b0df9aad32 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.ops/string_substr/subview.pass.cpp @@ -0,0 +1,129 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// constexpr basic_string_view<_CharT, _Traits> subview(size_type __pos = 0, size_type __n = npos) const; + +#include +#include +#include +#include +#include + +#include "constexpr_char_traits.h" +#include "make_string.h" +#include "min_allocator.h" +#include "test_allocator.h" +#include "test_macros.h" + +#define CS(S) MAKE_CSTRING(CharT, S) + +template +constexpr void test() { + std::basic_string s{CS("Hello cruel world!"), AllocT{}}; + + { // With a default position and a character length. + + // Also check if subview() is a const-qualified. + assert(std::as_const(s).subview() == CS("Hello cruel world!")); + + // Check it the return type of subview() is correct. + std::same_as> decltype(auto) sv = s.subview(); + assert(sv == CS("Hello cruel world!")); + } + + { // Check with different position and length. + + // With a explict position and a character length. + assert(s.subview(6, 5) == CS("cruel")); + + // From the beginning of the string with a explicit character length. + assert(s.subview(0, 5) == CS("Hello")); + + // To the end of string with the default character length. + assert(s.subview(12) == CS("world!")); + + // From the beginning to the end of the string with explicit values. + assert(s.subview(0, s.size()) == CS("Hello cruel world!")); + } + + // Test if exceptions are thrown correctly. +#ifndef TEST_HAS_NO_EXCEPTIONS + if (!std::is_constant_evaluated()) { + { // With a position that is out of range. + try { + s.subview(s.size() + 1); + assert(false && "Expected std::out_of_range exception"); + } catch ([[maybe_unused]] const std::out_of_range& ex) { + LIBCPP_ASSERT(std::string(ex.what()) == "string_view::subview"); + } catch (...) { + assert(false && "Expected std::out_of_range exception"); + } + } + + { // With a position that is out of range and a 0 character length. + try { + s.subview(s.size() + 1, 0); + assert(false && "Expected std::out_of_range exception"); + } catch ([[maybe_unused]] const std::out_of_range& ex) { + LIBCPP_ASSERT(std::string(ex.what()) == "string_view::subview"); + } catch (...) { + assert(false && "Expected std::out_of_range exception"); + } + } + + { // With a position that is out of range and a some character length. + try { + s.subview(s.size() + 1, 1); + assert(false && "Expected std::out_of_range exception"); + } catch ([[maybe_unused]] const std::out_of_range& ex) { + LIBCPP_ASSERT(std::string(ex.what()) == "string_view::subview"); + } catch (...) { + assert(false && "Expected std::out_of_range exception"); + } + } + } +#endif +} + +template +constexpr void test() { + test, std::allocator>(); + test, min_allocator>(); + test, safe_allocator>(); + test, test_allocator>(); + + test, std::allocator>(); + test, min_allocator>(); + test, safe_allocator>(); + test, test_allocator>(); +} + +constexpr bool test() { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif +#ifndef TEST_HAS_NO_CHAR8_T + test(); +#endif + test(); + test(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + 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..b135cce7aa2c4 --- /dev/null +++ b/libcxx/test/std/strings/string.view/string.view.ops/subview.pass.cpp @@ -0,0 +1,127 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// constexpr basic_string_view subview(size_type pos = 0, +// size_type n = npos) const; // freestanding-deleted + +#include +#include +#include +#include + +#include "constexpr_char_traits.h" +#include "make_string.h" +#include "test_macros.h" + +#define CS(S) MAKE_CSTRING(CharT, S) + +template +constexpr void test() { + std::basic_string_view sv{CS("Hello cruel world!")}; + + { // With a default position and a character length. + + // Check if subview() is a const-qualified. + assert(std::as_const(sv).subview() == CS("Hello cruel world!")); + + // Check if the return type of subview() is correct. + std::same_as> decltype(auto) subsv = sv.subview(); + assert(subsv == CS("Hello cruel world!")); + } + + { // Check with different position and length. + + // With a explict position and a character length. + assert(sv.subview(6, 5) == CS("cruel")); + + // From the beginning of the string with a explicit character length. + assert(sv.subview(0, 5) == CS("Hello")); + + // To the end of string with the default character length. + assert(sv.subview(12) == CS("world!")); + + // From the beginning to the end of the string with explicit values. + assert(sv.subview(0, sv.size()) == CS("Hello cruel world!")); + } + + // Test if exceptions are thrown correctly. +#ifndef TEST_HAS_NO_EXCEPTIONS + if (!std::is_constant_evaluated()) { + { // With a position that is out of range. + try { + sv.subview(sv.size() + 1); + assert(false && "Expected std::out_of_range exception"); + } catch ([[maybe_unused]] const std::out_of_range& ex) { + LIBCPP_ASSERT(std::string(ex.what()) == "string_view::subview"); + } catch (...) { + assert(false && "Expected std::out_of_range exception"); + } + } + + { // With a position that is out of range and a 0 character length. + try { + sv.subview(sv.size() + 1, 0); + assert(false && "Expected std::out_of_range exception"); + } catch ([[maybe_unused]] const std::out_of_range& ex) { + LIBCPP_ASSERT(std::string(ex.what()) == "string_view::subview"); + } catch (...) { + assert(false && "Expected std::out_of_range exception"); + } + } + + { // With a position that is out of range and a some character length. + try { + sv.subview(sv.size() + 1, 1); + assert(false && "Expected std::out_of_range exception"); + } catch ([[maybe_unused]] const std::out_of_range& ex) { + LIBCPP_ASSERT(std::string(ex.what()) == "string_view::subview"); + } catch (...) { + assert(false && "Expected std::out_of_range exception"); + } + } + } +#endif +} + +template +constexpr void test() { + test>(); + test>(); + test>(); + test>(); + + test>(); + test>(); + test>(); + test>(); +} + +constexpr bool test() { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif +#ifndef TEST_HAS_NO_CHAR8_T + test(); +#endif + test(); + test(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index d9317e00e3f4a..2c0c304d5c2f4 100644 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -1335,6 +1335,11 @@ def add_version_header(tc): "values": {"c++23": 202110}, "headers": ["string"], }, + { + "name": "__cpp_lib_string_subview", + "values": {"c++26": 202506}, + "headers": ["string", "string_view"], + }, { "name": "__cpp_lib_string_udls", "values": {"c++14": 201304},