sparrow 2.4.0
C++20 idiomatic APIs for the Apache Arrow Columnar Format
Loading...
Searching...
No Matches
iterator.hpp
Go to the documentation of this file.
1// Copyright 2024 Man Group Operations Limited
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#pragma once
16
17#include <compare>
18#include <iterator>
19#include <limits>
20#include <memory>
21#include <type_traits>
22#include <utility>
23
25
26namespace sparrow
27{
28 namespace detail
29 {
30 // Since C++20, iterators do not require dereference to return an
31 // lvalue, and therefore they do not require to provide operator->.
32 // However, because it is such a common operation, and because we
33 // don't want to propagate special cases for lvalue / rvalue handling
34 // everywhere in generic code, the iterator_base class provides an
35 // implementation of operator->, that returns either the address of
36 // an lvalue, or the address of a proxy on an rvalue.
37
38 // operator-> dispatcher for proxy references
39 template <class Reference, class Pointer>
41 {
42 struct proxy
43 {
44 explicit proxy(const Reference& x) noexcept
45 : m_reference(x)
46 {
47 }
48
49 explicit proxy(Reference&& x) noexcept
50 : m_reference(std::move(x))
51 {
52 }
53
54 Reference* operator->() noexcept
55 {
56 return std::addressof(m_reference);
57 }
58
59 private:
60
61 Reference m_reference;
62 };
63
65
66 [[nodiscard]] static result_type apply(const Reference& x)
67 {
68 return result_type(x);
69 }
70
71 [[nodiscard]] static result_type apply(Reference&& x)
72 {
73 return result_type(std::move(x));
74 }
75 };
76
77 // operator-> dispatcher for true references
78 template <class T, class Pointer>
79 struct operator_arrow_dispatcher<T&, Pointer>
80 {
81 using result_type = Pointer;
82
83 static result_type apply(T& x)
84 {
85 return std::addressof(x);
86 }
87 };
88 }
89
90 // Base class implementing the interface of a standard iterator
91 // that models the given IteratorConcept. Only the specializations
92 // for specific IteratorConcept values are defined. Currently,
93 // output_iterator_tag and input_iterator_tag are not supported.
94 // This allows a lot of simplifications regarding the definitions of
95 // postfix increment operators and related methods.
96 template <class Derived, class IteratorConcept, class Element, class Reference, class Difference>
98
99 // Utility class for accessing private methods
100 // of iterators.
102 {
103 template <class It>
104 static typename It::reference dereference(const It& it)
105 {
106 return it.dereference();
107 }
108
109 template <class It>
110 static void increment(It& it)
111 {
112 it.increment();
113 }
114
115 template <class It>
116 static void decrement(It& it)
117 {
118 it.decrement();
119 }
120
121 template <class It>
122 static void advance(It& it, typename It::difference_type n)
123 {
124 it.advance(n);
125 }
126
127// On MSVC and GCC < 13, those methods need to be public to be accessible by friend functions.
128#if _WIN32 || __GNUC__ < 13
129
130 public:
131
132#endif
133 template <class It>
134 [[nodiscard]] static typename It::difference_type distance_to(const It& lhs, const It& rhs)
135 {
136 return lhs.distance_to(rhs);
137 }
138
139 template <class It>
140 [[nodiscard]] static bool equal(const It& lhs, const It& rhs)
141 {
142 return lhs.equal(rhs);
143 }
144
145 template <class It>
146 [[nodiscard]] static bool less_than(const It& lhs, const It& rhs)
147 {
148 return lhs.less_than(rhs);
149 }
150
151// On MSVC and GCC < 13, those methods need to be public to be accessible by friend functions.
152#if _WIN32 || __GNUC__ < 13
153
154 private:
155
156#endif
157
158 template <class Derived, class E, class T, class R, class D>
159 friend class iterator_root_base;
160
161 template <class It>
162 friend typename It::difference_type operator-(const It&, const It&);
163
164 template <class It>
165 friend bool operator==(const It&, const It&);
166
167 template <class It>
168 friend std::strong_ordering operator<=>(const It&, const It&);
169 };
170
171 // Specialization of iterator_root_base for forward iterator.
172 template <class Derived, class Element, class Reference, class Difference>
173 class iterator_root_base<Derived, Element, std::forward_iterator_tag, Reference, Difference>
174 {
175 private:
176
177 using operator_arrow_dispatcher = detail::operator_arrow_dispatcher<Reference, Element*>;
178
179 public:
180
181 using derived_type = Derived;
182 using value_type = std::remove_cv_t<Element>;
183 using reference = Reference;
184 using pointer = typename operator_arrow_dispatcher::result_type;
185 using difference_type = Difference;
186 using iterator_category = std::forward_iterator_tag;
187 using iterator_concept = std::forward_iterator_tag;
188
189 reference operator*() const
190 {
191 return iterator_access::dereference(this->derived());
192 }
193
194 pointer operator->() const
195 {
196 return operator_arrow_dispatcher::apply(*this->derived());
197 }
198
199 derived_type& operator++()
200 {
201 iterator_access::increment(this->derived());
202 return this->derived();
203 }
204
205 derived_type operator++(int)
206 {
207 derived_type tmp(this->derived());
208 ++*this;
209 return tmp;
210 }
211
212 friend inline bool operator==(const derived_type& lhs, const derived_type& rhs)
213 {
214 return iterator_access::equal(lhs, rhs);
215 }
216
217 protected:
218
219 [[nodiscard]] derived_type& derived()
220 {
221 return *static_cast<derived_type*>(this);
222 }
223
224 [[nodiscard]] const derived_type& derived() const
225 {
226 return *static_cast<const derived_type*>(this);
227 }
228 };
229
230 // Specialization of iterator_root_base for bidirectional iterator.
231 template <class Derived, class Element, class Reference, class Difference>
232 class iterator_root_base<Derived, Element, std::bidirectional_iterator_tag, Reference, Difference>
233 : public iterator_root_base<Derived, Element, std::forward_iterator_tag, Reference, Difference>
234 {
235 public:
236
237 using base_type = iterator_root_base<Derived, Element, std::forward_iterator_tag, Reference, Difference>;
238 using derived_type = typename base_type::derived_type;
239 using iterator_category = std::bidirectional_iterator_tag;
240 using iterator_concept = std::bidirectional_iterator_tag;
241
242 derived_type& operator--()
243 {
244 iterator_access::decrement(this->derived());
245 return this->derived();
246 }
247
248 derived_type operator--(int)
249 {
250 derived_type tmp(this->derived());
251 --*this;
252 return tmp;
253 }
254 };
255
256 // Specialization of iterator_root_base for random access iterator.
257 template <class Derived, class Element, class Reference, class Difference>
258 class iterator_root_base<Derived, Element, std::random_access_iterator_tag, Reference, Difference>
259 : public iterator_root_base<Derived, Element, std::bidirectional_iterator_tag, Reference, Difference>
260 {
261 public:
262
264 using derived_type = typename base_type::derived_type;
265 using reference = typename base_type::reference;
266 using difference_type = typename base_type::difference_type;
267 using iterator_category = std::random_access_iterator_tag;
268 using iterator_concept = std::random_access_iterator_tag;
269
271 {
272 iterator_access::advance(this->derived(), n);
273 return this->derived();
274 }
275
277 {
278 derived_type tmp(it);
279 return tmp += n;
280 }
281
283 {
284 derived_type tmp(it);
285 return tmp += n;
286 }
287
289 {
290 iterator_access::advance(this->derived(), -n);
291 return this->derived();
292 }
293
295 {
296 derived_type tmp(it);
297 return tmp -= n;
298 }
299
301 {
302 return iterator_access::dereference(this->derived() + n);
303 }
304
305 friend inline difference_type operator-(const derived_type& lhs, const derived_type& rhs)
306 {
307 return iterator_access::distance_to(rhs, lhs);
308 }
309
310 friend inline std::strong_ordering operator<=>(const derived_type& lhs, const derived_type& rhs)
311 {
312 if (iterator_access::less_than(lhs, rhs))
313 {
314 return std::strong_ordering::less;
315 }
316 else if (iterator_access::equal(lhs, rhs))
317 {
318 return std::strong_ordering::equal;
319 }
320 else
321 {
322 return std::strong_ordering::greater;
323 }
324 }
325 };
326
327 // Specialization of iterator_root_base for contiguous iterator.
328 template <class Derived, class Element, class Reference, class Difference>
329 class iterator_root_base<Derived, Element, std::contiguous_iterator_tag, Reference, Difference>
330 : public iterator_root_base<Derived, Element, std::random_access_iterator_tag, Reference, Difference>
331 {
332 public:
333
334#ifdef __APPLE__
335 // Apple Clang wrong implementation of contiguous_iterator concept
337 using element_type = typename base_type::value_type;
338#endif
339 using iterator_concept = std::contiguous_iterator_tag;
340 };
341
342 /*
343 * @class iterator_base
344 * @brief Base class for iterator
345 *
346 * iterator_base is a CRTP base class that implements the interface of standard iterators,
347 * based on functions that must be supplied by a derived iterator class.
348 *
349 * The derived iterator class must define methods implementing the iterator's core behavior.
350 * The following table describes the expressions that must be valid, depending on the concept
351 * modeled by the derived iterator.
352 *
353 *
354 * | Concept | Expression | Implementation |
355 * | ------------- | ------------------- | ---------------------------- |
356 * | forward | it.dereference() | Access the value referred to |
357 * | forward | it.equal(jit) | Compare for equality |
358 * | forward | it.increment() | Advance by one position |
359 * | bidirectional | it.decrement() | Retreat by one position |
360 * | random access | it.advance(n) | Advance by n positions |
361 * | random access | it.distance_to(jit) | Compute the distance |
362 * | random access | it.less_than(jit) | Compare for inequality |
363 *
364 * These methods should be private and the derived iterator should declare sparrow::iterator_access
365 * as a friend class so that `iterator_base` implementation can access those methods.
366 *
367 * @tparam Derived The type inheriting from this CRTP base class
368 * @tparam Element The element type of the iterator, used to compute the `value_type` inner type.
369 * Element should be `value_type` iterators, and `const value_type` for const iterators.
370 * @tparam IteratorConcept The tag corresponding to the concept satisfied by the iterator.
371 * `input_iterator_tag` and `output_iterator_tag` are not supported.
372 * @tparam Reference The return type of the dereferencing operation. By default, `Element&`.
373 * @tparam Difference The unsigned integer used as the `difference_type`. By default, `std::ptrdiff_t`.
374 *
375 * @remark By default, the `iterator_category` inner type is the same as the `iterator_concept` inner type
376 * (except for contiguous_iterator, where it is `random_access_iterator_tag`). This can be overloaded
377 * in the inheriting iterator class.
378 */
379 template <class Derived, class Element, class IteratorConcept, class Reference = Element&, class Difference = std::ptrdiff_t>
380 class iterator_base : public iterator_root_base<Derived, Element, IteratorConcept, Reference, Difference>
381 {
382 };
383
384 /*
385 * @class iterator_adaptor
386 * @brief generic iterator adaptor
387 *
388 * This class forwards the calls to its "base" iterator, i.e.
389 * the iterator it adapts. Iterator adaptor classes should
390 * inherit from this class and redefine the private methods
391 * they need only.
392 */
393 template <
394 class Derived,
395 class Iter,
396 class Element,
397 class IteratorConcept = typename std::iterator_traits<Iter>::iterator_category,
398 class Reference = std::iter_reference_t<Iter>,
399 class Difference = std::iter_difference_t<Iter>>
400 class iterator_adaptor : public iterator_base<Derived, Element, IteratorConcept, Reference, Difference>
401 {
402 public:
403
406 using reference = typename base_type::reference;
407 using difference_type = typename base_type::difference_type;
408 using iterator_type = Iter;
409
410 constexpr iterator_adaptor() = default;
411
412 constexpr explicit iterator_adaptor(const iterator_type& iter)
413 : p_iter(iter)
414 {
415 }
416
417 [[nodiscard]] constexpr const iterator_type& base() const
418 {
419 return p_iter;
420 }
421
422 private:
423
424 constexpr reference dereference() const
425 {
426 return *p_iter;
427 }
428
429 constexpr void increment()
430 {
431 ++p_iter;
432 }
433
434 constexpr void decrement()
435 {
436 --p_iter;
437 }
438
439 constexpr void advance(difference_type n)
440 {
441 p_iter += n;
442 }
443
444 [[nodiscard]] constexpr difference_type distance_to(const self_type& rhs) const
445 {
446 return rhs.p_iter - p_iter;
447 }
448
449 [[nodiscard]] constexpr bool equal(const self_type& rhs) const
450 {
451 return p_iter == rhs.p_iter;
452 }
453
454 [[nodiscard]] constexpr bool less_than(const self_type& rhs) const
455 {
456 return p_iter < rhs.p_iter;
457 }
458
459 iterator_type p_iter = {};
460
461 friend class iterator_access;
462 };
463
464 /*
465 * @class pointer_iterator
466 * @brief iterator adaptor for pointers
467 *
468 * pointer_iterator gives an iterator API to a pointer.
469 * @tparam T the pointer to adapt
470 */
471 template <class T>
473
474 template <class T>
475 class pointer_iterator<T*>
476 : public iterator_adaptor<pointer_iterator<T*>, T*, T, std::contiguous_iterator_tag>
477 {
478 public:
479
480 using self_type = pointer_iterator<T*>;
482 using iterator_type = typename base_type::iterator_type;
483
484 constexpr pointer_iterator() = default;
485
486 explicit constexpr pointer_iterator(iterator_type p)
487 : base_type(p)
488 {
489 }
490
491 template <class U>
492 requires std::convertible_to<U*, iterator_type>
493 explicit constexpr pointer_iterator(U* u)
494 : base_type(iterator_type(u))
495 {
496 }
497 };
498
499 template <class T>
500 [[nodiscard]] constexpr pointer_iterator<T*> make_pointer_iterator(T* t)
501 {
502 return pointer_iterator<T*>(t);
503 }
504
505 /*
506 * @class pointer_index_iterator_base
507 * @brief CRTP base for iterators that store a pointer and an integer index.
508 *
509 * Provides default implementations of all iterator operations based on comparing
510 * and advancing the index. Derived classes must implement `dereference()` and may
511 * override `advance()`, `distance_to()`, `equal()`, or `less_than()` when they need
512 * stricter precondition checks or different arithmetic semantics.
513 *
514 * The stored members are `protected` so that derived-class overrides can access them
515 * directly:
516 * - `p_object` of type `Ptr*`
517 * - `m_index` of type `IndexType`
518 *
519 * @tparam Derived The concrete iterator class (CRTP).
520 * @tparam Ptr The pointed-to object type (e.g. `const array`). `p_object` has
521 * type `Ptr*`.
522 * @tparam Element The iterator's `value_type`.
523 * @tparam Reference The iterator's `reference` type.
524 * @tparam IteratorTag The iterator category tag.
525 * @tparam IndexType The integer type used for the index (default: `std::size_t`).
526 * @tparam Difference The iterator's `difference_type` (default: `std::ptrdiff_t`).
527 */
528 template <
529 class Derived,
530 class Ptr,
531 class Element,
532 class Reference,
533 class IteratorTag,
534 class IndexType = std::size_t,
535 class Difference = std::ptrdiff_t>
537 : public iterator_base<Derived, Element, IteratorTag, Reference, Difference>
538 {
539 public:
540
541 using size_type = IndexType;
542 using difference_type = Difference;
543
544 protected:
545
546 Ptr* p_object = nullptr;
547 IndexType m_index = 0;
548
549 constexpr pointer_index_iterator_base() noexcept = default;
550
551 constexpr pointer_index_iterator_base(Ptr* p, IndexType idx) noexcept
552 : p_object(p)
553 , m_index(idx)
554 {
555 }
556
557 constexpr void increment() noexcept(SPARROW_CONTRACTS_THROW_ON_FAILURE == 0)
558 {
559 SPARROW_ASSERT_TRUE(m_index < std::numeric_limits<size_type>::max());
560 ++m_index;
561 }
562
563 constexpr void decrement() noexcept(SPARROW_CONTRACTS_THROW_ON_FAILURE == 0)
564 {
566 --m_index;
567 }
568
569 constexpr void advance(Difference n) noexcept(SPARROW_CONTRACTS_THROW_ON_FAILURE == 0)
570 {
571 if constexpr (std::is_signed_v<IndexType>)
572 {
573 m_index += static_cast<IndexType>(n);
574 }
575 else
576 {
577 SPARROW_ASSERT_TRUE(n >= 0 || static_cast<IndexType>(-n) <= m_index);
578 m_index = static_cast<IndexType>(static_cast<Difference>(m_index) + n);
579 }
580 }
581
582 [[nodiscard]] constexpr Difference
583 distance_to(const Derived& rhs) const noexcept(SPARROW_CONTRACTS_THROW_ON_FAILURE == 0)
584 {
585 SPARROW_ASSERT_TRUE(p_object == rhs.p_object);
586 return static_cast<Difference>(rhs.m_index) - static_cast<Difference>(m_index);
587 }
588
589 [[nodiscard]] constexpr bool equal(const Derived& rhs) const noexcept
590 {
591 return p_object == rhs.p_object && m_index == rhs.m_index;
592 }
593
594 [[nodiscard]] constexpr bool
595 less_than(const Derived& rhs) const noexcept(SPARROW_CONTRACTS_THROW_ON_FAILURE == 0)
596 {
597 SPARROW_ASSERT_TRUE(p_object == rhs.p_object);
598 return m_index < rhs.m_index;
599 }
600
601 friend class iterator_access;
602 };
603
604 template <class InputIt, std::integral Distance>
605 [[nodiscard]] constexpr InputIt next(InputIt it, Distance n)
606 {
607 std::advance(it, n);
608 return it;
609 }
610}
friend bool operator==(const It &, const It &)
friend It::difference_type operator-(const It &, const It &)
friend std::strong_ordering operator<=>(const It &, const It &)
static bool less_than(const It &lhs, const It &rhs)
Definition iterator.hpp:146
friend class iterator_root_base
Definition iterator.hpp:159
static It::difference_type distance_to(const It &lhs, const It &rhs)
Definition iterator.hpp:134
static bool equal(const It &lhs, const It &rhs)
Definition iterator.hpp:140
iterator_adaptor< Derived, Iter, Element, IteratorConcept, Reference, Difference > self_type
Definition iterator.hpp:405
constexpr iterator_adaptor(const iterator_type &iter)
Definition iterator.hpp:412
constexpr const iterator_type & base() const
Definition iterator.hpp:417
constexpr iterator_adaptor()=default
iterator_base< Derived, Element, IteratorConcept, Reference, Difference > base_type
Definition iterator.hpp:404
friend difference_type operator-(const derived_type &lhs, const derived_type &rhs)
Definition iterator.hpp:305
friend std::strong_ordering operator<=>(const derived_type &lhs, const derived_type &rhs)
Definition iterator.hpp:310
iterator_root_base< Derived, Element, std::bidirectional_iterator_tag, Reference, Difference > base_type
Definition iterator.hpp:263
constexpr void advance(Difference n) noexcept(SPARROW_CONTRACTS_THROW_ON_FAILURE==0)
Definition iterator.hpp:569
constexpr pointer_index_iterator_base() noexcept=default
constexpr Difference distance_to(const Derived &rhs) const noexcept(SPARROW_CONTRACTS_THROW_ON_FAILURE==0)
Definition iterator.hpp:583
constexpr void increment() noexcept(SPARROW_CONTRACTS_THROW_ON_FAILURE==0)
Definition iterator.hpp:557
constexpr bool equal(const Derived &rhs) const noexcept
Definition iterator.hpp:589
constexpr void decrement() noexcept(SPARROW_CONTRACTS_THROW_ON_FAILURE==0)
Definition iterator.hpp:563
constexpr bool less_than(const Derived &rhs) const noexcept(SPARROW_CONTRACTS_THROW_ON_FAILURE==0)
Definition iterator.hpp:595
#define SPARROW_ASSERT_TRUE(expr__)
#define SPARROW_CONTRACTS_THROW_ON_FAILURE
Definition contracts.hpp:24
constexpr pointer_iterator< T * > make_pointer_iterator(T *t)
Definition iterator.hpp:500
SPARROW_API bool operator==(const array &lhs, const array &rhs)
Compares the content of two arrays.
constexpr InputIt next(InputIt it, Distance n)
Definition iterator.hpp:605
Extensions to the C++ standard library.
static result_type apply(Reference &&x)
Definition iterator.hpp:71
static result_type apply(const Reference &x)
Definition iterator.hpp:66