Stroika Library 3.0d24
 
Loading...
Searching...
No Matches
Iterator.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Traversal_Iterator_h_
5#define _Stroika_Foundation_Traversal_Iterator_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <compare>
10#include <iterator>
11#include <memory>
12#include <optional>
13#include <string>
14
15#include "Stroika/Foundation/Common/Common.h"
17
18/**
19 *
20 * \file
21 *
22 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
23 *
24 * TODO
25 * @todo https://github.com/SophistSolutions/Stroika/issues/580 (STK-446) - AssertExternallySynchronizedChecker
26 *
27 * @todo Consider if we want to make the promise currently defined below in Equals()
28 * about iterating two originally equal iterators. The trouble is - this doesn
29 * work with generators!!! -- REVIEW-- LGP 2013-12-30
30 *
31 * THIS IS BAD AND MUST BE REWRITEN - NOT WAHT WE WANT - TOO STRONG A PROMISE.
32 *
33 * @todo Speed tweaks
34 *
35 * The Major Design limitation of this approach to iterators is that it requires a non-inlinable
36 * function call per iteration (roughly). Basically - if you pass a callback into an iterator rep
37 * then to APPLY that function on each iteration requires a non-inlinable (indirect through pointer)
38 * function call, or if you do the reverse, and directly use the iterator so no you can inline
39 * apply the function, you must call a virtual function for each iteration to bump the next pointer.
40 *
41 * Fundamentally - you have multiple polymorphism (on representation of container and thing to apply
42 * at each iteration).
43 *
44 * Two tricks:
45 * (1) Paging.
46 * Read a bunch at a time. This is tricky to implement and doesn't affect overall computational
47 * complexity (because it reduces number of virtual calls by a constant factor). But if that
48 * constant factor is big enough - 10-100-1000? - that still could be relevant pragmatically.
49 *
50 * The biggest challenge is preserving the existing safety and patch semantics generically,
51 * in light of update during iteration, and making sure for uses where that doesn't help its
52 * not allowed to hurt.
53 *
54 * (2) special case common combinations. For example - at the point in the code where you have:
55 * Iterator<T> i;
56 * for (; i != e.end (); ++i) {
57 * do_this();
58 * }
59 *
60 * One could put in special purpose code for the Iterator<T>::operator++ that said:
61 * if (dynamic_cast<Sequence_Array::_IRep*> (myIRep) != nullptr) {
62 * then peek and cheat...
63 * }
64 * Maybe when the iterator is constructed - it checks for a couple important types
65 * and sets a flag, so the only cost when this doesn't work is checking that bool flag.
66 * And the benefit in the more common case is you avoid the virtual function call! so the it++ can be
67 * inlined (a big win often times).
68 *
69 */
70
71namespace Stroika::Foundation::Traversal {
72
73 namespace Support {
74
75 /**
76 * Identical type to std::iterator<> - but duplicated here because std::iterator<> was deprecated in C++17.
77 * We just need a handy way to capture all the defaults/properties for our iterator class.
78 * EXCEPT no iterator_category - because we want to be able to change that in subclasses (eg. Iterator<T> is a forward_iterator, but BidirectionalIterator<T> is a bidirectional_iterator, and RandomAccessIterator<T> is a random_access_iterator).
79 */
80 template <typename T, typename DIFF = ptrdiff_t, typename POINTER = const T*, typename REFERENCE = const T&>
82 using value_type = T;
83 using difference_type = DIFF;
84 using pointer = POINTER;
85 using reference = REFERENCE;
86 };
87
88 /**
89 * A concept for iterator traits.
90 *
91 * Checks all the key proprties of an Iterator TRAITS object except iterator_category, since
92 * that is generally NOT supplied to an Iterator<> class (its intrinsic to Iterator and its subtypes what
93 * category they provide - not a supplied trait).
94 */
95 template <typename TRAITS>
96 concept IIteratorTraits = requires (TRAITS) {
97 typename TRAITS::value_type;
98 typename TRAITS::difference_type;
99 typename TRAITS::pointer;
100 typename TRAITS::reference;
101 };
102
103 }
104
105 /**
106 * \brief An Iterator<T> is a copyable object which allows traversing the contents of some container.
107 *
108 * \@todo EXPLAIN HOW THIS IS CONNECTED TO c++20 'ranges'
109 *
110 * An Iterator<T> is typically associated with some container (that is being iterated over)
111 * and which allows traversal from start to finish.
112 * (The iterator itself essentially provides a notion of *start* to *finish*).
113 *
114 * There need not actually be a 'container' object. Other 'iterators' can be created, so long as
115 * they act like an iterator.
116 *
117 * An Iterator<T> is a copyable object which can safely be used to capture (copy) the state of
118 * iteration and continue iterating from that spot.
119 *
120 * An Iterator<T> be be thought of as (const) referencing a container (or other information source)
121 *
122 * It is (since Stroika 2.1b14) illegal to use an iterator after its underlying container
123 * has been modified (rule as in STL, but unlike most STLs, Stroika will automatically detect such
124 * illegal use in debug builds).
125 *
126 * Iterators CAN be used to MODIFY a container, but not directly - only by passing that iterator as an
127 * argument to a container method (such as Remove). Here the iterator cannot actually update the container
128 * but acts as an marker/indicator of what element to update. Such APIs will optionally return an updated iterator,
129 * so that you can continue with iteration (if desired).
130 *
131 * \note in Stroika 3.0d24, we renamed Done () to AtEnd (), and the old name is deprecated.
132 * PLUS, we HAD the rule that once at iterator was DONE, it could NEVER be UNDONE. But
133 * that rule is no longer in place. Bidirectional iterators that reach the end, can be moved backward
134 * to continue iterating backwards. This will still be often be true of ordinary iterators, but it may not be true
135 * for some.
136 *
137 * \note PRIOR to Stroika 2.1b14 it was true that
138 *
139 * "If the underlying container is modified, the iterator will be automatically
140 * updated to logically account for that update. Iterators are robust in the presence
141 * of changes to their underlying container. Adding or removing items from a container
142 * will not invalidate the iteration."
143 *
144 * "Different kinds of containers can make further guarantees about the behavior of iterators
145 * in the presence of container modifications. For example a SequenceIterator will always
146 * traverse any items added after the current traversal index, and will never traverse items
147 * added with an index before the current traversal index.
148 *
149 * But this is NO LONGER TRUE.
150 *
151 * \par Example Usage
152 * \code
153 * for (Iterator<T> i = container.MakeIterator (); not i.AtEnd (); i.Next ()) {
154 * f (i.Current ());
155 * }
156 * \endcode
157 *
158 * or:
159 * \code
160 * for (Iterator<T> i = container.begin (); i; ++i) {
161 * f (*i);
162 * }
163 * \endcode
164 *
165 * or:
166 * \code
167 * for (Iterator<T> i = container.begin (); i != container.end (); ++i) {
168 * f (*i);
169 * }
170 * \endcode
171 *
172 * or:
173 * \code
174 * for (T i : container) {
175 * f (i);
176 * }
177 * \endcode
178 *
179 * Key Differences between Stroika Iterators and STL Iterators:
180 *
181 * 1. Stroika iterators (in debug builds) will detect if they are used after
182 * the underlying container has changed (some STL's may do this too?)
183 *
184 * 2. Stroika iterators carry around their 'AtEnd' state all in one object.
185 * For compatibility with existing C++ idiom, and some C++11 language features,
186 * Stroika iterators allow use of container.end() (or i == container.end())
187 * to check for if an iterator is AtEnd. But internally,
188 * Stroika just checks i.AtEnd(), and so can users of Stroika iterators.
189 *
190 * 3. Stroika iterators are not 'random access' (see BidirectionalIterator, and RandomAccessIterator).
191 * They just go forwards, one step at a time.
192 *
193 * 4. In STL, reverse iterators are a special type, incompatible with regular iterators.
194 * In Stroika, reverse iterators are also created with rbegin(), rend (), but
195 * their type is no different than regular iterators.
196 * <<<< NYI >>>>
197 *
198 * Some Rules about Iterators:
199 *
200 * 1. Iterators can be copied. They always refer to the same
201 * place they did before the copy, and the old iterator is unaffected
202 * by iteration in the new iterator.
203 *
204 * Interesting Design (Implementation) Notes:
205 *
206 * - We considered a design for Current() - where it would dynamically
207 * grab the current value, as opposed to being defined to be frozen/copied
208 * at the time of iteration.
209 *
210 * The advantage of the path not taken is that if you iterated several times without
211 * examining the current value, and if the cost of copying was relatively large, this
212 * definition would have worked out better.
213 *
214 * However, because I think its far more frequent that the copies are cheap, the
215 * user will want to look at each value, and the cost in terms of thread locking
216 * and probably virtual function calls, the current approach of freezing / copying
217 * on iteration seemed better.
218 *
219 * \note Design Note
220 * Until Stroika 2.1d6, Iterator<> used CopyOnWrite (COW) - SharedByValue, instead of unique_ptr.
221 *
222 * SharedByValue costs a bit more when the iterators are never copied. But saves a lot of cost when iterators
223 * are copied (cuz with unique_ptr they need to actually be cloned).
224 *
225 * I DID run some simple tests to see how often we even use the Clone method. It turns out - quite rarely.
226 * And most can be eliminated by slightly better Move constructor support on the iterator class.
227 *
228 * \note Requires Concepts:
229 * o copyable<T>
230 * o constructible_from<optional<T>, T>
231 *
232 * \note Satisfies Concepts:
233 * o regular<Iterator<T>> // implies copyable/movable/equality_comparable
234 * o forward_iterator<Iterator<T>>
235 * o sentinel_for<default_sentinel_t, Iterator<T>>
236 *
237 * @see Iterable<T>
238 *
239 * \note \em Thread-Safety
240 *
241 * Iterator<T> instances are \em not thread-safe. That is - they cannot be read and
242 * or written from multiple threads at a time.
243 *
244 * However, given how Iterators are meant to be, and are typically, used, this presents
245 * no problem.
246 *
247 * They can be safely transferred across threads, and the underlying things being iterated over
248 * can be safely and transparently read/written from other threads
249 *
250 * <a href="Thread-Safety.md#C++-Standard-Thread-Safety">C++-Standard-Thread-Safety</a>
251 */
252 template <typename T, Support::IIteratorTraits ITERATOR_TRAITS = Support::DefaultIteratorTraits<T>>
253 class Iterator {
254 public:
255 static_assert (constructible_from<optional<T>, T>,
256 "Must be able to create optional<T> to use Iterator, because Iterator uses this internally");
257 static_assert (copyable<T>); // cannot use as type constraint on T cuz fails?? ill understood - probably complex usages with incomplete types..
258
259 public:
260 /**
261 * \brief difference_type = typename ITERATOR_TRAITS::difference_type;
262 */
263 using difference_type = typename ITERATOR_TRAITS::difference_type;
264
265 public:
266 /**
267 * \brief value_type = typename ITERATOR_TRAITS::value_type;
268 */
269 using value_type = typename ITERATOR_TRAITS::value_type;
270
271 public:
272 /**
273 * \brief pointer = typename ITERATOR_TRAITS::pointer;
274 */
275 using pointer = typename ITERATOR_TRAITS::pointer;
276
277 public:
278 /**
279 * \brief reference = typename ITERATOR_TRAITS::reference;
280 */
281 using reference = typename ITERATOR_TRAITS::reference;
282
283 public:
284 /**
285 * \brief iterator_category = forward_iterator_tag;
286 *
287 * \note The Stroika Iterator class models the forward_iterator concept. Prior to Stroika 3.0d24
288 * this was a templated parameter, never really used.
289 *
290 * (note - actually something between input_iterator and forward_iterator - must consider more?s)
291 *
292 * \note Prior to Stroika 3.0d24 we captured the iterator_category as a template parameter, but
293 * that caused problems with subclassing (eg. BidirectionalIterator, RandomAccessIterator) and the IRep type.
294 */
295 using iterator_category = forward_iterator_tag;
296
297 public:
298 class IRep;
299
300 public:
301 /**
302 */
303 using RepSmartPtr [[deprecated ("Since Stroika v3.0d1 - just use unique_ptr<IRep> directly")]] = unique_ptr<IRep>;
304
305 public:
306 /**
307 * \brief This overload is usually not called directly. Instead, iterators are
308 * usually created from a container (eg. Sequence<T>{}.begin()).
309 *
310 * Iterators are safely copyable, preserving their current position.
311 *
312 * CTOR overload taking nullptr - is the same as GetEmptyIterator ()
313 *
314 * \note since copy constructor calls Clone_, these can throw exceptions but move copies/assignments are no-except
315 *
316 * \note for ranges to work, the return type of Iterable<T>::end () must be a 'sentinel_for' compatible concept
317 * which implies it must be default constructible. So interpret default construction of Iterator as meaning empty/end sentinel.
318 *
319 * \pre RequireNotNull (rep.get ())
320 *
321 * \note constructor with argument default_sentinel_t - creates an end iterator
322 */
323 Iterator (const unique_ptr<IRep>& rep) noexcept;
324 Iterator (unique_ptr<IRep>&& rep) noexcept;
325 Iterator (Iterator&& src) noexcept;
326 Iterator (const Iterator& src);
327 constexpr Iterator (const default_sentinel_t&) noexcept;
328 constexpr Iterator (nullptr_t) noexcept;
329 constexpr Iterator () noexcept;
330
331 public:
332 /**
333 * \brief Iterators are safely copyable, preserving their current position. Copy-Assigning could throw since it probably involves a Clone()
334 */
335 nonvirtual Iterator& operator= (Iterator&& rhs) noexcept;
336 nonvirtual Iterator& operator= (const Iterator& rhs);
337
338 public:
339 /**
340 * \brief Return the Current value pointed to by the Iterator<T> (same as Current())
341 *
342 * Support for range-based-for, and STL style iteration in general (containers must also
343 * support begin, end).
344 *
345 * This function is a synonym for @ref Current();
346 *
347 * \note Until Stroika 2.1r1, this returned T, and was switched to return const T& on the theory that it might
348 * perform better, but testing has not confirmed that (though this does appear to be existing practice in things like STL).
349 *
350 * \note It is illegal (but goes undetected) to hang onto (and use) the reference returned past when the iterator is next modified
351 *
352 * \note use of for (auto& c : Iterable<>) won't work with Stroika Iterator<> classes since operator* returns const reference only
353 * as we don't allow updating containers by fiddling with the iterator only.
354 */
355 nonvirtual const T& operator* () const;
356
357 public:
358 /**
359 * \brief Return a pointer to the current value pointed to by the Iterator<T> (like Current())
360 *
361 * This function allows you to write i->b, where i is an iterator and b is a member of the type
362 * iterated over by i.
363 *
364 * Note - the lifetime of this pointer is short - only until the next operation on the wrapper
365 * class instance Iterator<T>.
366 */
367 nonvirtual const value_type* operator->() const;
368
369 public:
370 /**
371 * \brief Advance iterator; support for range-based-for, and STL style iteration in
372 * general (containers must also support begin, end).
373 *
374 * Advance iterator; support for range-based-for, and STL style iteration in general
375 * (containers must also support begin, end).
376 *
377 * operator++ can be called anytime as long as AtEnd () is not true (must be called
378 * prior to operator++). It then it iterates to the item in the container (i.e. it
379 * changes the value returned by Current).
380 *
381 * Note - the value return by Current() is frozen (until the next operator++() call)
382 * when this method is called. Its legal to update the underlying container, but those
383 * values won't be seen until the next iteration.
384 *
385 * \note As of Stroika v3.0d1, we again support post-increment (operator++ (int)), NOT because its useful, but because its required by std+c++
386 * to be considered an input iterator (see https://en.cppreference.com/w/cpp/iterator/weakly_incrementable).
387 *
388 * It is slower, and not recommended. BUT because of this - supported.
389 */
390 nonvirtual Iterator& operator++ ();
391 nonvirtual Iterator operator++ (int);
392
393 public:
394 /**
395 * \pre operator++ can be called 'i' times (on a copy of this), and the result returned.
396 *
397 * \note don't use unsigned 'i' because that works less well with overloads and ambiguity.
398 * \note similar to std::advance, but allows for simpler usage (i + n)
399 * \req i >= 0
400 */
401 nonvirtual Iterator operator+ (ptrdiff_t i) const;
402
403 public:
404 /**
405 * \brief return not AtEnd ()
406 *
407 * \em Design Note:
408 * I HATE type punning - which this is. And I may want to lose this.
409 *
410 * However, this API works beautifully with Iterable<>::Find - and perhaps other things that
411 * return iterators.
412 *
413 * also, it allows
414 * Iterator<T> n = ...;
415 * while (n) {
416 * }
417 * not sure that's better than while (not n.AtEnd ())???
418 */
419 nonvirtual explicit operator bool () const;
420
421 public:
422 /**
423 * \brief Equals () checks if two iterators are equal to one another (point to the same position in the sequence).
424 *
425 * \em NB: Equals () is the same notion of equality as used by STL iterators.
426 *
427 * \em NB: It is \pre required that the two iterators being compared must come from the same source, or from the special source nullptr.
428 *
429 * Very roughly, the idea is that to be 'equal' - two iterators must be iterating over the same source,
430 * and be up to the same position. The slight exception to this is that any two iterators that are AtEnd()
431 * are considered Equals (). This is mainly because we use a different representation for 'AtEnd'
432 * iterators.
433 *
434 * Note - for Equals. The following assertion will succeed:
435 *
436 * Iterator<T> x = getIterator();
437 * Iterator<T> y = x;
438 * x++;
439 * y++;
440 * Assert (Equals (x, y)); // assume x and y not already 'at end' then...
441 * // will always succeed (x++ and y++ )
442 *
443 * However,
444 * Iterator<T> x = getIterator();
445 * Iterator<T> y = x;
446 * x++;
447 * modify_underlying_container()
448 * y++;
449 * if (Equals (x, y)) {
450 * may or may not be so
451 * }
452 *
453 * Note that Equals is *commutative*.
454 */
455 nonvirtual bool operator== (const Iterator& rhs) const;
456 nonvirtual bool operator== (const default_sentinel_t& rhs) const;
457
458 public:
459 /**
460 * \brief Returns the value of the current item visited by the Iterator<T>, and is illegal to call if AtEnd()
461 *
462 * Current () returns the value of the current item visited by the Iterator<T>.
463 *
464 * Only one things can change the current value of Current():
465 * o any non-const method of the iterator
466 *
467 * Two subsequent calls to *it *cannot* return different values with no
468 * intervening (non-const) calls on the iterator.
469 *
470 * \pre not AtEnd ()
471 *
472 * operator*() is a common synonym for Current().
473 *
474 * @see operator*()
475 * @see operator++()
476 *
477 * \note Until Stroika 2.1r1, this returned T, and was switched to return const T& on the theory that it might
478 * perform better, but testing has not confirmed that (though this does appear to be existing practice in things like STL).
479 *
480 * \note It is illegal (but goes undetected) to hang onto (and use) the reference returned past when the iterator is next modified
481 *
482 * \note IRep::Current () returns optional<T>, and can be called while AtEnd
483 */
484 nonvirtual const T& Current () const;
485
486 public:
487 /**
488 * \brief AtEnd () means there is nothing left in this iterator (a synonym for (it == container.end ()).
489 *
490 * AtEnd () means there is nothing left to visit in this iterator.
491 *
492 * When an iterator is AtEnd(), it is illegal to call Current().
493 *
494 * Calling AtEnd() *may* change (initialize) the value which would be returned by the next
495 * call to Current().
496 *
497 * NB: There are *no* modifications to an underlying container which will directly change
498 * the value of AtEnd(). This value only changes the next time the cursor is advanced
499 * via a call to operator++();
500 *
501 * if it comes from container, then (it == container.end ()) is true iff it.AtEnd()
502 */
503 nonvirtual bool AtEnd () const;
504
505 public:
506 /**
507 * \brief Set to AtEnd and disassociate with owner.
508 *
509 * Equivalent to *this = GetEmptyIterator();
510 *
511 * @aliases clear ()
512 */
513 nonvirtual void reset ();
514
515 public:
516 /**
517 * \brief Set to AtEnd and disassociate with owner.
518 *
519 * Equivalent to *this = GetEmptyIterator();
520 *
521 * @aliases reset ()
522 */
523 nonvirtual void clear ();
524
525 public:
526 /**
527 * \brief Used by *someContainer*::end ()
528 *
529 * GetEmptyIterator () returns a special iterator which is always empty - always 'at the end'.
530 * This is handy in implementing STL-style 'if (a != b)' style iterator comparisons.
531 *
532 * \note this is something like the c++20 ranges sentinel idea, except that we don't use a separate type (perhaps a mistake on my part).
533 */
534 static constexpr default_sentinel_t GetEmptyIterator () noexcept;
535
536 public:
537 /**
538 * \brief Get a reference to the IRep owned by the iterator. This is an implementation detail,
539 * mainly intended for implementors.
540 *
541 * Get a reference to the IRep owned by the iterator.
542 * This is an implementation detail, mainly intended for implementors.
543 */
544 nonvirtual IRep& GetRep ();
545
546 public:
547 /**
548 * \brief Get a reference to the IRep owned by the iterator. This is an implementation detail,
549 * mainly intended for implementors.
550 */
551 nonvirtual const IRep& ConstGetRep () const;
552
553 public:
554 /**
555 * \brief Refresh the current iterator state based on what is in the underlying IRep
556 *
557 * Useful when you change the rep directly. This should VERY RARELY be needed - just in implementing iterator patching (say during a remove).
558 */
559 nonvirtual void Refresh ();
560
561 public:
562 /**
563 * \brief Invariant does nothing if !qStroika_Foundation_Debug_AssertionsChecked, but if qStroika_Foundation_Debug_AssertionsChecked, checks internal state and asserts in good shape
564 */
565 nonvirtual void Invariant () const noexcept;
566
567 private:
568 unique_ptr<IRep> fRep_;
569
570 protected:
571 // note that _fCurrentValue is MISSING, iff AtEnd
572 optional<T> _fCurrentValue;
573
574 private:
575 static unique_ptr<IRep> Clone_ (const IRep& rep);
576
577 public:
578 template <typename SHARED_T>
579 using PtrImplementationTemplate [[deprecated ("Since Stroika v3.0d1 - use unique_ptr directly")]] = unique_ptr<SHARED_T>;
580 template <typename SHARED_T, typename... ARGS_TYPE>
581 [[deprecated ("Since Stroika v3.0d1 - make_unique directly")]] static unique_ptr<SHARED_T> MakeSmartPtr (ARGS_TYPE&&... args)
582 {
583 return make_unique<SHARED_T> (forward<ARGS_TYPE> (args)...);
584 }
585 [[deprecated ("Since Stroika v3.0d34 Use AtEnd() instead")]]
586 nonvirtual bool Done () const
587 {
588 return AtEnd ();
589 }
590 };
591
592 /**
593 * \brief Implementation detail for iterator implementors.
594 *
595 * IRep is a support class used to implement the @ref Iterator<T> pattern.
596 *
597 * \note IRep subclasses are constructed already pointing at the first element.
598 * So a leading call to Current () can be used to fetch the first value
599 * and value.has_value() will be false if there were no values
600 *
601 * Subclassed by concrete container writers.
602 *
603 * \note Design Note:
604 * o More () -> optional<T> API combines operator++ and iterator != end ()
605 * o The reason it combines the two, is because they TYPICALLY are done together at the same time,
606 * and its a virtual call, so combining the two into a single call will most frequently be a
607 * performance optimization.
608 *
609 * \note Upgrade Note:
610 * In Stroika v3.0d24, we changed the API for Iterator<T, ITERATOR_TRAITS>::IRep, adding AtEnd ()
611 * and Current () pure virtual methods that must be overridden, and changing the API of More () to not
612 * take an 'advance' parameter, but always assume its true, and then just return optional<T> instead
613 * of taking it as a pointer parameter.
614 */
615 template <typename T, Support::IIteratorTraits ITERATOR_TRAITS>
616 class Iterator<T, ITERATOR_TRAITS>::IRep {
617 protected:
618 IRep () = default;
619
620 public:
621 virtual ~IRep () = default;
622
623 public:
624 using RepSmartPtr [[deprecated ("Since Stroika v3.0d1 - use unique_ptr<IRep> directly")]] = unique_ptr<IRep>;
625
626 public:
627 /**
628 * Clone() makes a copy of the state of this iterator, which can separately be tracked with Equals ()
629 * and/or More() to get values and move forward through the iteration.
630 */
631 virtual unique_ptr<IRep> Clone () const = 0;
632
633 public:
634 /**
635 * \brief
636 * Check if the iterator is at the end of the range.
637 *
638 * \note This value must be in agreement with the results returned by More (), and Current ().
639 */
640 virtual bool AtEnd () const = 0;
641
642 public:
643 /**
644 * \brief
645 * Return nullopt if it end of range, else return current value iterator points to.
646 */
647 virtual optional<T> Current () const = 0;
648
649 public:
650 /**
651 * \brief More () advances the iterator to the next position, and returns the value there (nullopt if at end).
652 *
653 * \pre not AtEnd()
654 *
655 * \em Design Note
656 * We chose to use a return value instead of reference argument to leverage RTO optimization, and allow
657 * value to be used to initialize value instead of requiring first default initialize (differs from pre Stroika v3.0b24).
658 *
659 * \em Design Note
660 * Standard C++ iterators separate advancing from testing if AtEnd. That is almost strictly better.
661 * However, standard c++ iterators don't require a virtual call per iteration. It is to mitigate
662 * that we combine those two operations into one call (not that unnaturally).
663 */
664 virtual optional<T> More () = 0;
665
666 public:
667 /**
668 * \brief two iterators must be iterating over the same source, and be up to the same position.
669 *
670 * \pre rhs != nullptr
671 *
672 * \pre this and rhs must be of the same dynamic type, and come from the same iterable object
673 *
674 * @see Iterator<T>::Equals for details
675 */
676 virtual bool Equals (const IRep* rhs) const = 0;
677
678#if qStroika_Foundation_Debug_AssertionsChecked
679 public:
680 /**
681 */
682 virtual void Invariant () const noexcept;
683#endif
684 };
685
686 /**
687 * \brief More clear way of writing '&*' - convert iterator to pointer.
688 *
689 * Sometimes (especially when interacting with low level code) its handy to convert an iterator
690 * to a pointer. This is always legal for a short period (@todo reference to docs/why).
691 *
692 * But the idiom is somewhat queer, and wrapping in this method makes it a bit more clear.
693 *
694 * \note This returns a const pointer for a const_iterator, and a pointer for a regular (non-cost) iterator.
695 */
696 template <typename ITERATOR>
697 constexpr typename iterator_traits<ITERATOR>::pointer Iterator2Pointer (ITERATOR i);
698
699 /**
700 * IInputIterator concept: std::input_iterator and iterated over values convertible to OF_T
701 *
702 * \note this does not require the input iterator is OF T objects, merely that the T objects it iterates over
703 * can be converted to OF_T objects.
704 */
705 template <typename ITERATOR, typename OF_T>
706 concept IInputIterator = input_iterator<ITERATOR> and is_convertible_v<std::iter_value_t<ITERATOR>, OF_T>;
707 static_assert (IInputIterator<Iterator<int>, int>);
708 static_assert (IInputIterator<Iterator<long int>, int>);
709 static_assert (IInputIterator<Iterator<int>, long int>);
710 static_assert (not IInputIterator<Iterator<string>, int>);
711
712 /**
713 * IForwardIterator concept: std::forward_iterator and iterated over values convertible to OF_T
714 *
715 * \note this does not require the forward iterator is OF T objects, merely that the T objects it iterates over
716 * can be converted to OF_T objects.
717 */
718 template <typename ITERATOR, typename OF_T>
719 concept IForwardIterator = forward_iterator<ITERATOR> and is_convertible_v<std::iter_value_t<ITERATOR>, OF_T>;
720 static_assert (IForwardIterator<Iterator<int>, int>);
721 static_assert (IForwardIterator<Iterator<long int>, int>);
722 static_assert (IForwardIterator<Iterator<int>, long int>);
723 static_assert (not IForwardIterator<Iterator<string>, int>);
724
725 // see Satisfies Concepts
726 // @todo would be nice to include these tests generically as part of template declaration, but cannot figure out how
727 // to get that working (probably due to when incomplete types evaluated) --LGP 2024-08-21
728 static_assert (forward_iterator<Iterator<int>>);
729 static_assert (regular<Iterator<int>>);
730 static_assert (sentinel_for<default_sentinel_t, Iterator<int>>);
731
732}
733
734/*
735 ********************************************************************************
736 ******************************* Implementation Details *************************
737 ********************************************************************************
738 */
739
740#include "Iterator.inl"
741
742#endif /*_Stroika_Foundation_Traversal_Iterator_h_ */
constexpr iterator_traits< ITERATOR >::pointer Iterator2Pointer(ITERATOR i)
More clear way of writing '&*' - convert iterator to pointer.
Definition Iterator.inl:254
Implementation detail for iterator implementors.
Definition Iterator.h:616
virtual optional< T > More()=0
More () advances the iterator to the next position, and returns the value there (nullopt if at end).
virtual optional< T > Current() const =0
Return nullopt if it end of range, else return current value iterator points to.
virtual bool Equals(const IRep *rhs) const =0
two iterators must be iterating over the same source, and be up to the same position.
virtual bool AtEnd() const =0
Check if the iterator is at the end of the range.
virtual unique_ptr< IRep > Clone() const =0
An Iterator<T> is a copyable object which allows traversing the contents of some container.
Definition Iterator.h:253
nonvirtual void reset()
Set to AtEnd and disassociate with owner.
Definition Iterator.inl:149
forward_iterator_tag iterator_category
iterator_category = forward_iterator_tag;
Definition Iterator.h:295
nonvirtual IRep & GetRep()
Get a reference to the IRep owned by the iterator. This is an implementation detail,...
Definition Iterator.inl:107
nonvirtual const IRep & ConstGetRep() const
Get a reference to the IRep owned by the iterator. This is an implementation detail,...
Definition Iterator.inl:113
typename ITERATOR_TRAITS::difference_type difference_type
difference_type = typename ITERATOR_TRAITS::difference_type;
Definition Iterator.h:263
typename ITERATOR_TRAITS::pointer pointer
pointer = typename ITERATOR_TRAITS::pointer;
Definition Iterator.h:275
nonvirtual void clear()
Set to AtEnd and disassociate with owner.
Definition Iterator.inl:154
typename ITERATOR_TRAITS::value_type value_type
value_type = typename ITERATOR_TRAITS::value_type;
Definition Iterator.h:269
nonvirtual void Invariant() const noexcept
Invariant does nothing if !qStroika_Foundation_Debug_AssertionsChecked, but if qStroika_Foundation_De...
Definition Iterator.inl:125
nonvirtual const T & Current() const
Returns the value of the current item visited by the Iterator<T>, and is illegal to call if AtEnd()
Definition Iterator.inl:135
typename ITERATOR_TRAITS::reference reference
reference = typename ITERATOR_TRAITS::reference;
Definition Iterator.h:281
static constexpr default_sentinel_t GetEmptyIterator() noexcept
Used by someContainer::end ()
Definition Iterator.inl:243
nonvirtual bool AtEnd() const
AtEnd () means there is nothing left in this iterator (a synonym for (it == container....
Definition Iterator.inl:143
nonvirtual void Refresh()
Refresh the current iterator state based on what is in the underlying IRep.
Definition Iterator.inl:119