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