Skip to content

Commit f064ac2

Browse files
committed
[libc++] Simplify the implementation of string::{append,assign,assign_range}
1 parent d7eade1 commit f064ac2

File tree

3 files changed

+62
-31
lines changed

3 files changed

+62
-31
lines changed

libcxx/include/string

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,8 @@ __concatenate_strings(const _Allocator& __alloc,
698698
__type_identity_t<basic_string_view<_CharT, _Traits> > __str1,
699699
__type_identity_t<basic_string_view<_CharT, _Traits> > __str2);
700700

701+
// This is true if we know for a fact that dereferencing the iterator won't access any part of the `string` we're
702+
// modifying if __addr_in_range(*it) returns false.
701703
template <class _Iter>
702704
inline const bool __string_is_trivial_iterator_v = false;
703705

@@ -1413,24 +1415,16 @@ public:
14131415
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s);
14141416
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(size_type __n, value_type __c);
14151417

1416-
template <class _InputIterator, __enable_if_t<__has_exactly_input_iterator_category<_InputIterator>::value, int> = 0>
1417-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
1418-
append(_InputIterator __first, _InputIterator __last) {
1419-
const basic_string __temp(__first, __last, __alloc_);
1420-
append(__temp.data(), __temp.size());
1421-
return *this;
1422-
}
1423-
1424-
template <class _ForwardIterator, __enable_if_t<__has_forward_iterator_category<_ForwardIterator>::value, int> = 0>
1418+
template <class _InputIterator>
14251419
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
1426-
append(_ForwardIterator __first, _ForwardIterator __last) {
1427-
size_type __sz = size();
1428-
size_type __cap = capacity();
1429-
size_type __n = static_cast<size_type>(std::distance(__first, __last));
1430-
if (__n == 0)
1431-
return *this;
1420+
append(_InputIterator __first, _InputIterator __last) {
1421+
if (__string_is_trivial_iterator_v<_InputIterator> && !__addr_in_range(*__first)) {
1422+
size_type __sz = size();
1423+
size_type __cap = capacity();
1424+
size_type __n = static_cast<size_type>(std::distance(__first, __last));
1425+
if (__n == 0)
1426+
return *this;
14321427

1433-
if (__string_is_trivial_iterator_v<_ForwardIterator> && !__addr_in_range(*__first)) {
14341428
if (__cap - __sz < __n)
14351429
__grow_by_without_replace(__cap, __sz + __n - __cap, __sz, __sz, 0);
14361430
__annotate_increase(__n);
@@ -1540,17 +1534,10 @@ public:
15401534
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& assign(const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s);
15411535
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& assign(size_type __n, value_type __c);
15421536

1543-
template <class _InputIterator, __enable_if_t<__has_exactly_input_iterator_category<_InputIterator>::value, int> = 0>
1537+
template <class _InputIterator>
15441538
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
15451539
assign(_InputIterator __first, _InputIterator __last) {
1546-
__assign_with_sentinel(__first, __last);
1547-
return *this;
1548-
}
1549-
1550-
template <class _ForwardIterator, __enable_if_t<__has_forward_iterator_category<_ForwardIterator>::value, int> = 0>
1551-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
1552-
assign(_ForwardIterator __first, _ForwardIterator __last) {
1553-
if (__string_is_trivial_iterator_v<_ForwardIterator>) {
1540+
if _LIBCPP_CONSTEXPR (__string_is_trivial_iterator_v<_InputIterator>) {
15541541
size_type __n = static_cast<size_type>(std::distance(__first, __last));
15551542
__assign_trivial(__first, __last, __n);
15561543
} else {
@@ -1563,8 +1550,7 @@ public:
15631550
# if _LIBCPP_STD_VER >= 23
15641551
template <_ContainerCompatibleRange<_CharT> _Range>
15651552
_LIBCPP_HIDE_FROM_ABI constexpr basic_string& assign_range(_Range&& __range) {
1566-
if constexpr (__string_is_trivial_iterator_v<ranges::iterator_t<_Range>> &&
1567-
(ranges::forward_range<_Range> || ranges::sized_range<_Range>)) {
1553+
if constexpr (__string_is_trivial_iterator_v<ranges::iterator_t<_Range>>) {
15681554
size_type __n = static_cast<size_type>(ranges::distance(__range));
15691555
__assign_trivial(ranges::begin(__range), ranges::end(__range), __n);
15701556

libcxx/test/std/strings/basic.string/string.modifiers/string_append/iterator.pass.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@
1111
// template<class InputIterator>
1212
// basic_string& append(InputIterator first, InputIterator last); // constexpr since C++20
1313

14-
#include <string>
1514
#include <cassert>
15+
#include <string>
16+
#include <vector>
1617

17-
#include "test_macros.h"
18-
#include "test_iterators.h"
19-
#include "min_allocator.h"
2018
#include "asan_testing.h"
19+
#include "min_allocator.h"
20+
#include "test_iterators.h"
21+
#include "test_macros.h"
2122

2223
template <class S, class It>
2324
TEST_CONSTEXPR_CXX20 void test(S s, It first, It last, S expected) {
@@ -221,6 +222,12 @@ TEST_CONSTEXPR_CXX20 void test_string() {
221222
s.append(MoveIt(It(std::begin(p))), MoveIt(It(std::end(p) - 1)));
222223
assert(s == "ABCD");
223224
}
225+
{
226+
std::vector<char> buffer(5, 'a');
227+
S s;
228+
s.append(single_pass_iterator<std::vector<char>>(buffer), single_pass_iterator<std::vector<char> >());
229+
assert(s == "aaaaa");
230+
}
224231
}
225232

226233
TEST_CONSTEXPR_CXX20 bool test() {

libcxx/test/support/test_iterators.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,44 @@ template <class It>
115115
cpp17_input_iterator(It) -> cpp17_input_iterator<It>;
116116
#endif
117117

118+
template <class Container>
119+
class single_pass_iterator {
120+
Container* container_;
121+
122+
public:
123+
using iterator_category = std::input_iterator_tag;
124+
using value_type = typename Container::value_type;
125+
using difference_type = typename Container::difference_type;
126+
using pointer = typename Container::pointer;
127+
using reference = typename Container::reference;
128+
129+
TEST_CONSTEXPR explicit single_pass_iterator() = default;
130+
TEST_CONSTEXPR explicit single_pass_iterator(Container& container)
131+
: container_(container.empty() ? nullptr : &container) {}
132+
133+
TEST_CONSTEXPR reference operator*() const { return container_->back(); }
134+
135+
TEST_CONSTEXPR_CXX14 single_pass_iterator& operator++() {
136+
container_->pop_back();
137+
if (container_->empty())
138+
container_ = nullptr;
139+
return *this;
140+
}
141+
142+
TEST_CONSTEXPR_CXX14 single_pass_iterator operator++(int) { return ++(*this); }
143+
144+
friend TEST_CONSTEXPR bool operator==(const single_pass_iterator& lhs, const single_pass_iterator& rhs) {
145+
return lhs.container_ == rhs.container_;
146+
}
147+
148+
friend TEST_CONSTEXPR bool operator!=(const single_pass_iterator& lhs, const single_pass_iterator& rhs) {
149+
return !(lhs == rhs);
150+
}
151+
152+
template <class T>
153+
void operator,(const T&) = delete;
154+
};
155+
118156
#if TEST_STD_VER > 17
119157
static_assert(std::input_iterator<cpp17_input_iterator<int*>>);
120158
#endif

0 commit comments

Comments
 (0)