kblib 0.2.3
General utilities library for modern C++
iterators.cpp
Go to the documentation of this file.
1#include "kblib/iterators.h"
2#include "kblib/build.h"
3
4#include "catch.hpp"
5
6#include <iterator>
7#include <list>
8#include <set>
9#include <sstream>
10
11TEST_CASE("to_pointer") {
12 auto smart_ptr = std::make_unique<int>(0);
13 auto* raw_pointer = kblib::to_pointer(smart_ptr);
14 CHECK(raw_pointer == smart_ptr.get());
15 const auto& smart_ptr_const_ref = smart_ptr;
16 auto* raw_pointer2 = kblib::to_pointer(smart_ptr_const_ref);
17 CHECK(raw_pointer2 == smart_ptr_const_ref.get());
18}
19
20TEST_CASE("range") {
21 // range supports iterators and other similar types.
22 std::vector<int> v(100);
23 CHECK(v.begin() == *kblib::range(v.begin(), v.end()).begin());
24 CHECK(v.end() == *kblib::range(v.begin(), v.end()).end());
25
26 std::vector<int> r{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
27 auto r2 = kblib::range(10);
28 CHECK(r.size() == r2.size());
29 CHECK(std::equal(r.begin(), r.end(), r2.begin()));
30 CHECK(r2[0] == 0);
31 CHECK(r2[9] == 9);
32 CHECK(r2[-1] == -1);
33 SECTION("size") {
34 CHECK(kblib::range(0, 10, 1).size() == 10);
35 CHECK(kblib::range(10).size() == 10);
36 CHECK(kblib::range(0, 10, 2).size() == 5);
37 CHECK(kblib::range(0, 1).size() == 1);
38 CHECK(kblib::range(0, 0).size() == 0);
39 CHECK(kblib::range(0, 0, 1).size() == 0);
40
41 CHECK(kblib::range(10, 20, 1).size() == 10);
42 CHECK(kblib::range(10, 20, 2).size() == 5);
43 CHECK(kblib::range(10, 11).size() == 1);
44 CHECK(kblib::range(10, 10).size() == 0);
45 CHECK(kblib::range(10, 10, 1).size() == 0);
46 }
47 SECTION("empty") {
48 CHECK(kblib::range(0, 0, 1).empty());
49 CHECK(kblib::range(0, 0, 2).empty());
50 CHECK(kblib::range(10, 10).empty());
51
52 CHECK(not kblib::range(0, 10, 1).empty());
53 CHECK(not kblib::range(0, 10, 2).empty());
54 }
55}
56
57TEST_CASE("range conversion") {
58 SECTION("vector") {
59 auto r = kblib::range(2, 5);
60 auto v = std::vector<int>(r);
61 CHECK(v.size() == r.size());
62 CHECK(std::equal(v.begin(), v.end(), r.begin()));
63 }
64 SECTION("string") {
65 auto r = kblib::range(+'a', 'z' + 1);
66 auto v = std::string(r);
67 CHECK(v == "abcdefghijklmnopqrstuvwxyz");
68 CHECK(v.size() == r.size());
69 CHECK(std::equal(v.begin(), v.end(), r.begin()));
70 }
71 auto x = std::set<int>(kblib::range(0, 10));
72}
73
74TEST_CASE("range comparison") {
75 auto equal = [](auto r1, auto r2) {
76 auto r1b = r1.begin();
77 auto r1e = r1.end();
78 auto r2b = r2.begin();
79 auto r2e = r2.end();
80 return std::distance(r1b, r1e) == std::distance(r2b, r2e)
81 and kblib::equal(r1b, r1e, r2b);
82 };
83
84 SECTION("equivalency") {
85
86 auto target10 = std::vector<int>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
87 auto target10m = std::vector<int>{0, -1, -2, -3, -4, -5, -6, -7, -8, -9};
88 auto target10r = std::vector<int>{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
89
90 CHECK(equal(kblib::range(0, 10), target10));
91 CHECK(equal(kblib::range(10), target10));
92 CHECK(equal(kblib::range(0, 10, kblib::incrementer{}), target10));
93
94 CHECK(equal(kblib::range(0, 10, 2), std::vector<int>{0, 2, 4, 6, 8}));
95
96 CHECK(equal(kblib::range(10, 0, -1), target10r));
97 CHECK(equal(kblib::range(10, 0, kblib::decrementer{}), target10r));
98
99 CHECK(equal(kblib::range(0, -10, -1), target10m));
100 CHECK(equal(kblib::range(0, -10), target10m));
101
102 CHECK(equal(kblib::range(0, 11, 2), kblib::range(0, 12, 2)));
103 }
104
105 SECTION("comparisons") {
106
107 CHECK(kblib::range(0, 0, 1) == kblib::range(1, 1, 2));
108 CHECK(kblib::range(100, 100, 1) == kblib::range(0, 0, 2));
109 CHECK(kblib::range(0, 10) != kblib::range(10, 0));
110 CHECK(kblib::range(0, 11, 2) != kblib::range(0, 10, 2));
111 CHECK(kblib::range(0, 0, 1) != kblib::range(0, 1, 2));
112 }
113
114 SECTION("buildiota equivalency") {
115 auto l = kblib::buildiota<std::list<int>>(10u, 10, -1);
116 CHECK(equal(kblib::range(10, 0, -1), l));
117 }
118}
119
120TEST_CASE("range from iterators") {
121 std::string str = "abcdefghijklmnopqrstuvwxyz";
122 // Just asserting that this loop compiles.
123 for ([[gnu::unused]] std::string::iterator c :
124 kblib::range(str.begin(), str.end())) {
125 }
126}
127
128TEST_CASE("ranges overflow") {
129 unsigned char c{};
130 for (auto i : kblib::range(static_cast<unsigned char>(255))) {
131 c = i;
132 }
133 CHECK(c == 254);
134}
135
136#if KBLIB_USE_CXX17
137
138TEST_CASE("magic_enumerate") {
139 std::vector<int> persistent{0, 1, 1, 2, 3, 5, 8};
140 SECTION("lvalue") {
141 for (auto&& [i, v] : kblib::magic_enumerate(persistent)) {
142 REQUIRE(&v == &persistent[i]);
143 }
144 }
145 SECTION("non-forwarding lvalue") {
146 for (auto& [i, v] : kblib::magic_enumerate(persistent)) {
147 REQUIRE(&v == &persistent[i]);
148 }
149 }
150 SECTION("copy") {
151 for (auto [i, v] : kblib::magic_enumerate(persistent)) {
152 REQUIRE(v == persistent[i]);
153 REQUIRE(&v != &persistent[i]);
154 }
155 }
156
157 SECTION("const lvalue") {
158 const auto& cp = persistent;
159 for (auto&& [i, v] : kblib::magic_enumerate(cp)) {
160 REQUIRE(&v == &cp[i]);
161 static_assert(std::is_const_v<std::remove_reference_t<decltype(v)>>,
162 "v must refer to const when the range is const");
163 }
164 }
165 SECTION("const reference") {
166 for (const auto& [i, v] : kblib::magic_enumerate(persistent)) {
167 REQUIRE(&v == &persistent[i]);
168 static_assert(
169 std::is_const_v<std::remove_reference_t<decltype(v)>>,
170 "v must refer to const when the bound reference is const");
171 }
172 }
173 SECTION("copy from const") {
174 const auto& cp = persistent;
175 for (auto [i, v] : kblib::magic_enumerate(cp)) {
176 REQUIRE(v == persistent[i]);
177 REQUIRE(&v != &persistent[i]);
178 static_assert(
179 not std::is_const_v<std::remove_reference_t<decltype(v)>>,
180 "v must not be const when copied from a const range");
181 }
182 }
183 SECTION("const copy from non-const") {
184# if defined(__clang__)
185 // Suppress warning for intentional behavior
186# pragma clang diagnostic push
187# pragma clang diagnostic ignored "-Wrange-loop-analysis"
188# endif
189 for (const auto [i, v] : kblib::magic_enumerate(persistent)) {
190 REQUIRE(v == persistent[i]);
191 REQUIRE(&v != &persistent[i]);
192 static_assert(std::is_const_v<std::remove_reference_t<decltype(v)>>,
193 "v must refer to const in a const copy");
194 }
195# if defined(__clang__)
196# pragma clang diagnostic pop
197# endif
198 }
199
200 SECTION("iterators") {
201 for (auto&& [i, v] :
202 kblib::magic_enumerate(persistent.cbegin(), persistent.cend())) {
203 REQUIRE(&v == &persistent[i]);
204 }
205 }
206 SECTION("mutable iterators") {
207 std::vector<int> range{0, 1, 2, 3, 4, 5, 6, 7};
208 for (auto&& [i, v] : kblib::magic_enumerate(range.begin(), range.end())) {
209 v = 0;
210 }
211 REQUIRE(std::all_of(range.begin(), range.end(),
212 [](int v) { return v == 0; }));
213 }
214 SECTION("reverse iterators") {
215 std::vector<int> reversed{7, 6, 5, 4, 3, 2, 1, 0};
216 int last = -1;
217 for (auto&& [i, v] :
218 kblib::magic_enumerate(reversed.rbegin(), reversed.rend())) {
219 REQUIRE(v == static_cast<int>(i));
220 ++v;
221 last = v;
222 }
223 REQUIRE(last == reversed.front());
224 }
225
226 SECTION("temporary") {
227 for (auto&& [i, v] :
228 kblib::magic_enumerate(std::vector<int>(persistent))) {
229 REQUIRE(v == persistent[i]);
230 REQUIRE(&v != &persistent[i]);
231 }
232 }
233 SECTION("input iterators") {
234 std::istringstream is{"0 0 1 1 2 2 3 3 4 4 5 5 6 6"};
235 for (auto&& [i, v] : kblib::magic_enumerate(
236 std::istream_iterator<int>(is), std::istream_iterator<int>())) {
237 REQUIRE(v == static_cast<int>(i) / 2);
238 }
239 }
240 SECTION("copied input iterators") {
241 std::istringstream is{"0 0 1 1 2 2 3 3 4 4 5 5 6 6"};
242 for (auto [i, v] : kblib::magic_enumerate(std::istream_iterator<int>(is),
243 std::istream_iterator<int>())) {
244 REQUIRE(v == static_cast<int>(i) / 2);
245 }
246 }
247
248 SECTION("move-only") {
249 std::vector<std::unique_ptr<int>> ptr_vec(10);
250 for ([[maybe_unused]] auto&& [i, ptr] : kblib::magic_enumerate(ptr_vec)) {
251 REQUIRE(not ptr);
252 }
253 }
254
255 SECTION("array") {
256 int arr[5] = {0, 1, 2, 3, 4};
257 for (auto [i, v] : kblib::magic_enumerate(arr)) {
258 REQUIRE(&arr[i] != &v);
259 REQUIRE(static_cast<int>(i) == v);
260 REQUIRE(arr[i] == v);
261 ++v;
262 REQUIRE(arr[i] != v);
263 }
264 }
265
266 SECTION("array by ref") {
267 int arr[5] = {0, 1, 2, 3, 4};
268 for (auto& [i, v] : kblib::magic_enumerate(arr)) {
269 REQUIRE(&arr[i] == &v);
270 }
271 }
272
273 SECTION("array by const ref") {
274 int arr[5] = {0, 1, 2, 3, 4};
275 for (const auto& [i, v] : kblib::magic_enumerate(arr)) {
276 REQUIRE(&arr[i] == &v);
277 }
278 }
279
280 SECTION("array by forwarding ref") {
281 int arr[5] = {0, 1, 2, 3, 4};
282 for (auto&& [i, v] : kblib::magic_enumerate(arr)) {
283 REQUIRE(&arr[i] == &v);
284 }
285 }
286}
287
288TEST_CASE("cry_enumerate") {
289 std::vector<int> persistent{0, 1, 1, 2, 3, 5, 8};
290 SECTION("lvalue") {
291 for (auto&& [i, v] : kblib::cry_enumerate(persistent)) {
292 REQUIRE(v == persistent[i]);
293 REQUIRE(&v == &persistent[i]);
294 }
295 }
296 SECTION("non-forwarding lvalue") {
297 for (auto& [i, v] : kblib::cry_enumerate(persistent)) {
298 REQUIRE(&v == &persistent[i]);
299 }
300 }
301 SECTION("copy") {
302 for (auto [i, v] : kblib::cry_enumerate(persistent)) {
303 REQUIRE(v == persistent[i]);
304 REQUIRE(&v != &persistent[i]);
305 }
306 }
307
308 SECTION("const lvalue") {
309 const auto& cp = persistent;
310 for (auto&& [i, v] : kblib::cry_enumerate(cp)) {
311 REQUIRE(&v == &cp[i]);
312 static_assert(
313 std::is_const<
314 typename std::remove_reference<decltype(v)>::type>::value,
315 "v must refer to const when the range is const");
316 }
317 }
318 SECTION("const reference") {
319 for (const auto& [i, v] : kblib::cry_enumerate(persistent)) {
320 REQUIRE(&v == &persistent[i]);
321 static_assert(
322 std::is_const<
323 typename std::remove_reference<decltype(v)>::type>::value,
324 "v must refer to const when the bound reference is const");
325 }
326 }
327 SECTION("copy from const") {
328 const auto& cp = persistent;
329 for (auto [i, v] : kblib::cry_enumerate(cp)) {
330 REQUIRE(v == persistent[i]);
331 REQUIRE(&v != &persistent[i]);
332 // This approach can't do this
333 /*static_assert(
334 not std::is_const<
335 typename std::remove_reference<decltype(v)>::type>::value,
336 "v must not be const when copied from a const range");*/
337 }
338 }
339 SECTION("const copy from non-const") {
340# if defined(__clang__)
341 // Suppress warning for intentional behavior
342# pragma clang diagnostic push
343# pragma clang diagnostic ignored "-Wrange-loop-analysis"
344# endif
345 for (const auto [i, v] : kblib::cry_enumerate(persistent)) {
346 REQUIRE(v == persistent[i]);
347 REQUIRE(&v != &persistent[i]);
348 static_assert(
349 std::is_const<
350 typename std::remove_reference<decltype(v)>::type>::value,
351 "v must refer to const in a const copy");
352 }
353# if defined(__clang__)
354# pragma clang diagnostic pop
355# endif
356 }
357
358 SECTION("iterators") {
359 for (auto&& [i, v] :
360 kblib::cry_enumerate(persistent.cbegin(), persistent.cend())) {
361 REQUIRE(&v == &persistent[i]);
362 }
363 }
364 SECTION("mutable iterators") {
365 std::vector<int> range{0, 1, 2, 3, 4, 5, 6, 7};
366 for (auto&& [i, v] : kblib::cry_enumerate(range.begin(), range.end())) {
367 v = 0;
368 }
369 REQUIRE(std::all_of(range.begin(), range.end(),
370 [](int v) { return v == 0; }));
371 }
372 SECTION("reverse iterators") {
373 std::vector<int> reversed{7, 6, 5, 4, 3, 2, 1, 0};
374 for (auto&& [i, v] :
375 kblib::cry_enumerate(reversed.rbegin(), reversed.rend())) {
376 REQUIRE(v == static_cast<int>(i));
377 }
378 }
379
380 SECTION("temporary") {
381 for (auto&& [i, v] : kblib::cry_enumerate(std::vector<int>(persistent))) {
382 REQUIRE(v == persistent[i]);
383 REQUIRE(&v != &persistent[i]);
384 }
385 }
386 SECTION("input iterators") {
387 std::istringstream is{"0 0 1 1 2 2 3 3 4 4 5 5 6 6"};
388 for (auto&& [i, v] : kblib::cry_enumerate(std::istream_iterator<int>(is),
389 std::istream_iterator<int>())) {
390 REQUIRE(v == static_cast<int>(i) / 2);
391 }
392 }
393 SECTION("copied input iterators") {
394 std::istringstream is{"0 0 1 1 2 2 3 3 4 4 5 5 6 6"};
395 for (auto [i, v] : kblib::cry_enumerate(std::istream_iterator<int>(is),
396 std::istream_iterator<int>())) {
397 REQUIRE(v == static_cast<int>(i) / 2);
398 }
399 }
400
401 SECTION("move-only") {
402 std::vector<std::unique_ptr<int>> ptr_vec(10);
403 for (auto&& [i, ptr] : kblib::cry_enumerate(ptr_vec)) {
404 (void)i;
405 }
406 }
407
408 SECTION("array") {
409 int arr[5] = {0, 1, 2, 3, 4};
410 for (auto [i, v] : kblib::cry_enumerate(arr)) {
411 REQUIRE(static_cast<int>(i) == v);
412 }
413 }
414
415 SECTION("array by ref") {
416 int arr[5] = {0, 1, 2, 3, 4};
417 for (auto& [i, v] : kblib::cry_enumerate(arr)) {
418 REQUIRE(static_cast<int>(i) == v);
419 }
420 }
421
422 SECTION("array by const ref") {
423 int arr[5] = {0, 1, 2, 3, 4};
424 for (const auto& [i, v] : kblib::cry_enumerate(arr)) {
425 REQUIRE(static_cast<int>(i) == v);
426 }
427 }
428
429 SECTION("array by forwarding ref") {
430 int arr[5] = {0, 1, 2, 3, 4};
431 for (auto&& [i, v] : kblib::cry_enumerate(arr)) {
432 REQUIRE(static_cast<int>(i) == v);
433 }
434 }
435}
436
437#endif
Provides by-value algorithms which produce containers.
TEST_CASE("to_pointer")
Definition: iterators.cpp:11
This file provides some iterators, ranges, iterator/range adapters, and operations that can be perfor...
constexpr auto size(const C &c) -> decltype(c.size())
Definition: fakestd.h:366
auto magic_enumerate(It begin, EIt end) -> enumerator_t< It, EIt >
Allow access to indexes while using range-based for loops.
Definition: iterators.h:989
constexpr auto to_pointer(P &&p) noexcept -> auto *
Gets a raw pointer out of any smart pointer or iterator you might pass in, without dereferencing it o...
Definition: iterators.h:72
typename std::remove_reference< T >::type remove_reference_t
Abbreviated name for std::remove_reference<T>::type for C++14.
Definition: traits.h:438
auto cry_enumerate(Range &&range) -> auto
constexpr auto range(Value min, Value max, Delta step=0) -> range_t< Value, Delta >
Constructs a range from beginning, end, and step amount. The range is half-open, that is min is in th...
Definition: iterators.h:621
constexpr auto all_of(InputIt begin, InputIt end, UnaryPredicate pred) -> enable_if_t< is_input_iterator< InputIt >::value, bool >
Determine if pred is true for every element of the range.
Definition: algorithm.h:970
constexpr auto equal(InputIt1 first1, InputIt1 last1, InputIt2 first2) -> bool
Definition: fakestd.h:960
A struct which decrements anything it is added to. Suitable for use as a Delta type for range_t.
Definition: iterators.h:589
A struct which increments anything it is added to. Suitable for use as a Delta type for range_t.
Definition: iterators.h:563