diff --git a/libcxx/include/string b/libcxx/include/string --- a/libcxx/include/string +++ b/libcxx/include/string @@ -333,6 +333,9 @@ constexpr bool contains(charT c) const noexcept; // C++2b constexpr bool contains(const charT* s) const; // C++2b + template + constexpr void resize_and_overwrite(size_type n, Operation op); // since C++23 + bool __invariants() const; }; @@ -985,7 +988,19 @@ _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 void resize(size_type __n) {resize(__n, value_type());} _LIBCPP_CONSTEXPR_AFTER_CXX17 void reserve(size_type __requested_capacity); - _LIBCPP_INLINE_VISIBILITY void __resize_default_init(size_type __n); + +#if _LIBCPP_STD_VER > 20 + template + _LIBCPP_HIDE_FROM_ABI constexpr + void resize_and_overwrite(size_type __n, _Operation __op) { + if (__n > capacity()) + __resize_default_init(__n); + + __erase_to_end(_VSTD::move(__op)(data(), __n)); + } +#endif + + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 void __resize_default_init(size_type __n); _LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_INLINE_VISIBILITY void reserve() _NOEXCEPT {shrink_to_fit();} _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 void shrink_to_fit() _NOEXCEPT; @@ -1050,7 +1065,7 @@ _LIBCPP_CONSTEXPR_AFTER_CXX17 basic_string& append(const value_type* __s); _LIBCPP_CONSTEXPR_AFTER_CXX17 basic_string& append(size_type __n, value_type __c); - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 void __append_default_init(size_type __n); template @@ -2748,7 +2763,7 @@ } template -inline void +inline void _LIBCPP_CONSTEXPR_AFTER_CXX17 basic_string<_CharT, _Traits, _Allocator>::__append_default_init(size_type __n) { if (__n) @@ -3478,7 +3493,7 @@ } template -inline void +inline void _LIBCPP_CONSTEXPR_AFTER_CXX17 basic_string<_CharT, _Traits, _Allocator>::__resize_default_init(size_type __n) { size_type __sz = size(); diff --git a/libcxx/test/std/strings/basic.string/string.capacity/resize_and_overwrite.pass.cpp b/libcxx/test/std/strings/basic.string/string.capacity/resize_and_overwrite.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.capacity/resize_and_overwrite.pass.cpp @@ -0,0 +1,150 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// template +// void resize_and_overwrite(size_type n, Operation op) + +#include +#include + +#include "make_string.h" + +template +class move_only_functor { +public: + constexpr move_only_functor(Operation op) : op_{op} {} + move_only_functor(const move_only_functor&) = delete; + move_only_functor(move_only_functor&&) = default; + move_only_functor& operator=(const move_only_functor&) = delete; + move_only_functor& operator=(move_only_functor&&) = default; + ~move_only_functor() = default; + + template + constexpr size_t operator()(Args... args) && { + return op_(args...); + } + +private: + Operation op_; +}; + +template +constexpr void test_exact_size(S s, S s2) { + const auto new_size = s.size() * 2; + auto func = [&](auto* begin, size_t size) { + assert(size == new_size); + std::copy(s.begin(), s.end(), begin); + std::copy(s.begin(), s.end(), begin + s.size()); + return new_size; + }; + { + S s3{s2}; + s3.resize_and_overwrite(new_size, func); + assert(s3 == s + s); + } + { + S s3{s2}; + s3.resize_and_overwrite(new_size, move_only_functor{func}); + } +} + +template +constexpr void test_oversized(S s, S s3) { + const auto new_size = s.size() * 3; + const auto actual_size = s.size() * 2; + auto func = [&](auto* begin, size_t size) { + assert(size == new_size); + std::copy(s.begin(), s.end(), begin); + std::copy(s.begin(), s.end(), begin + s.size()); + return actual_size; + }; + { + S s2{s3}; + s2.resize_and_overwrite(new_size, func); + assert(s2.size() == actual_size); + assert(s2.capacity() >= new_size); + assert(s2 == s + s); + } + { + S s2{s3}; + s2.resize_and_overwrite(new_size, move_only_functor{func}); + assert(s2.size() == actual_size); + assert(s2.capacity() >= new_size); + assert(s2 == s + s); + } +} + +template +constexpr void test_undersized(S s, S s3) { + const auto new_size = s.size() + s.size() / 2; + auto func = [&](auto* begin, size_t size) { + assert(size == new_size); + std::copy(s.begin(), s.end(), begin); + std::copy(s.begin(), s.begin() + s.size() / 2, begin + s.size()); + return size; + }; + { + S s2{s3}; + s2.resize_and_overwrite(new_size, func); + assert(s2 == s + s.substr(0, s.size() / 2)); + } + { + S s2{s3}; + s2.resize_and_overwrite(new_size, move_only_functor{func}); + assert(s2 == s + s.substr(0, s.size() / 2)); + } +} + +template +constexpr void test_all(S s1, S s2) { + test_exact_size(s1, s2); + test_oversized(s1, s2); + test_undersized(s1, s2); +} + +template +constexpr bool test_copy_strings(S2 s) { + test_all(MAKE_STRING(S, ""), s); + test_all(MAKE_STRING(S, "Banane"), s); + test_all(MAKE_STRING(S, "long long string so no SSO"), s); + + return true; +} + +template +constexpr bool test_overwrite_str() { + test_copy_strings>({}); + test_copy_strings>(MAKE_STRING(S, "short string")); + test_copy_strings>(MAKE_STRING(S, "long long string so no SSO")); + + return true; +} + +#define CONSTEXPR_TEST(...) \ + __VA_ARGS__; \ + static_assert(__VA_ARGS__) + +void test() { + CONSTEXPR_TEST(test_overwrite_str()); + CONSTEXPR_TEST(test_overwrite_str()); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + CONSTEXPR_TEST(test_overwrite_str()); +#endif + CONSTEXPR_TEST(test_overwrite_str()); + CONSTEXPR_TEST(test_overwrite_str()); +} + +int main(int, char**) { + test(); + + return 0; +}