1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
10
11 // <ranges>
12
13 // constexpr iterator& operator++();
14 // constexpr iterator operator++(int);
15
16 #include <ranges>
17
18 #include <array>
19 #include <cassert>
20 #include <concepts>
21 #include <functional>
22 #include <span>
23 #include <type_traits>
24 #include <utility>
25
26 #include "../types.h"
27 #include "test_iterators.h"
28 #include "test_macros.h"
29
30 struct TrackingPred : TrackInitialization {
31 using TrackInitialization::TrackInitialization;
operator ()TrackingPred32 constexpr bool operator()(int x, int y) const { return x <= y; }
33 };
34
35 template <class Iterator, IsConst Constant>
test()36 constexpr void test() {
37 using Sentinel = sentinel_wrapper<Iterator>;
38 using Underlying = View<Iterator, Sentinel>;
39 using ChunkByView = std::ranges::chunk_by_view<Underlying, std::ranges::less_equal>;
40 using ChunkByIterator = std::ranges::iterator_t<ChunkByView>;
41
42 auto make_chunk_by_view = [](auto& arr) {
43 View view{Iterator{arr.data()}, Sentinel{Iterator{arr.data() + arr.size()}}};
44 return ChunkByView{std::move(view), std::ranges::less_equal{}};
45 };
46
47 // Increment the iterator when it won't find another satisfied value after begin()
48 {
49 std::array array{0, 1, 2, 3, 4};
50 ChunkByView view = make_chunk_by_view(array);
51 ChunkByIterator it = view.begin();
52
53 std::same_as<ChunkByIterator&> decltype(auto) result = ++it;
54 assert(&result == &it);
55 assert(result == view.end());
56 assert(result == std::default_sentinel);
57 }
58
59 // Increment the iterator and it finds another value after begin()
60 {
61 std::array array{1, 2, 3, -1, -2, -3};
62 int const* second_chunk = array.data() + 3;
63 ChunkByView view = make_chunk_by_view(array);
64
65 ChunkByIterator it = view.begin();
66 ++it;
67 assert(base((*it).begin()) == second_chunk);
68 }
69
70 // Increment advances all the way to the end of the range
71 {
72 std::array array{1, 2, 3, 4, 1};
73 ChunkByView view = make_chunk_by_view(array);
74
75 ChunkByIterator it = view.begin();
76 ++it;
77 assert(base((*it).begin()) == array.data() + 4);
78 }
79
80 // Increment an iterator multiple times
81 {
82 std::array array{0, 1, 0, 2, 0, 3, 0, 4};
83 ChunkByView view = make_chunk_by_view(array);
84
85 ChunkByIterator it = view.begin();
86 assert(base((*it).begin()) == array.data());
87 ++it;
88 assert(base((*it).begin()) == array.data() + 2);
89 ++it;
90 assert(base((*it).begin()) == array.data() + 4);
91 ++it;
92 assert(base((*it).begin()) == array.data() + 6);
93 ++it;
94 assert(it == std::default_sentinel);
95 }
96
97 // Test with a predicate that takes by non-const reference
98 if constexpr (!std::to_underlying(Constant)) {
99 std::array array{1, 2, 3, -3, -2, -1};
100 View v{Iterator{array.data()}, Sentinel{Iterator{array.data() + array.size()}}};
101 auto view = std::views::chunk_by(std::move(v), [](int& x, int& y) { return x <= y; });
102
103 auto it = view.begin();
104 assert(base((*it).begin()) == array.data());
105 ++it;
106 assert(base((*it).begin()) == array.data() + 3);
107 }
108
109 // Test with a predicate that is invocable but not callable (i.e. cannot be called like regular function 'f()')
110 {
111 std::array array = {1, 2, 3, -3, -2, -1};
112 auto v = View{Iterator{array.data()}, Sentinel{Iterator{array.data() + array.size()}}} |
113 std::views::transform([](int x) { return IntWrapper{x}; });
114 auto view = std::views::chunk_by(std::move(v), &IntWrapper::lessEqual);
115
116 auto it = view.begin();
117 assert(base((*it).begin().base()) == array.data());
118 ++it;
119 assert(base((*it).begin().base()) == array.data() + 3);
120 }
121
122 // Make sure we do not make a copy of the predicate when we increment
123 // (we should be passing it to ranges::adjacent_find using std::ref)
124 {
125 bool moved = false, copied = false;
126 std::array array{1, 2, 1, 3};
127 View v{Iterator(array.data()), Sentinel(Iterator(array.data() + array.size()))};
128 auto view = std::views::chunk_by(std::move(v), TrackingPred(&moved, &copied));
129 assert(std::exchange(moved, false));
130 auto it = view.begin();
131 ++it;
132 it++;
133 assert(!moved);
134 assert(!copied);
135 }
136
137 // Check post-increment
138 {
139 std::array array{0, 1, 2, -3, -2, -1, -6, -5, -4};
140 ChunkByView view = make_chunk_by_view(array);
141 ChunkByIterator it = view.begin();
142
143 std::same_as<ChunkByIterator> decltype(auto) result = it++;
144 assert(result != it);
145 assert(base((*result).begin()) == array.data());
146 assert(base((*it).begin()) == array.data() + 3);
147
148 result = it++;
149 assert(base((*result).begin()) == array.data() + 3);
150 assert(base((*it).begin()) == array.data() + 6);
151
152 result = it++;
153 assert(base((*result).begin()) == array.data() + 6);
154 assert(it == std::default_sentinel);
155 }
156 }
157
tests()158 constexpr bool tests() {
159 test<forward_iterator<int*>, IsConst::no>();
160 test<bidirectional_iterator<int*>, IsConst::no>();
161 test<random_access_iterator<int*>, IsConst::no>();
162 test<contiguous_iterator<int*>, IsConst::no>();
163 test<int*, IsConst::no>();
164
165 test<forward_iterator<int const*>, IsConst::yes>();
166 test<bidirectional_iterator<int const*>, IsConst::yes>();
167 test<random_access_iterator<int const*>, IsConst::yes>();
168 test<contiguous_iterator<int const*>, IsConst::yes>();
169 test<int const*, IsConst::yes>();
170
171 return true;
172 }
173
main(int,char **)174 int main(int, char**) {
175 tests();
176 static_assert(tests());
177
178 return 0;
179 }
180