2009-07-11 19:23:20 +02:00
|
|
|
|
/*
|
2017-12-02 02:41:27 +01:00
|
|
|
|
ITER-ADAPTER.hpp - helpers for building simple forward iterators
|
2010-12-17 23:28:49 +01:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
Copyright (C)
|
|
|
|
|
|
2009, Hermann Vosseler <Ichthyostega@web.de>
|
2024-11-26 17:35:05 +01:00
|
|
|
|
2017,2024, Hermann Vosseler <Ichthyostega@web.de>
|
2010-12-17 23:28:49 +01:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
**Lumiera** is free software; you can redistribute it and/or modify it
|
|
|
|
|
|
under the terms of the GNU General Public License as published by the
|
|
|
|
|
|
Free Software Foundation; either version 2 of the License, or (at your
|
|
|
|
|
|
option) any later version. See the file COPYING for further details.
|
2010-12-17 23:28:49 +01:00
|
|
|
|
|
2009-07-11 19:23:20 +02:00
|
|
|
|
*/
|
|
|
|
|
|
|
2009-07-13 01:25:24 +02:00
|
|
|
|
/** @file iter-adapter.hpp
|
2017-12-02 02:41:27 +01:00
|
|
|
|
** Helper template(s) for creating *Lumiera Forward Iterators*.
|
2011-11-19 18:01:48 +01:00
|
|
|
|
** These are the foundation to build up iterator like types from scratch.
|
2009-07-15 07:26:49 +02:00
|
|
|
|
** Usually, these templates will be created and provided by a custom
|
|
|
|
|
|
** container type and accessed by the client through a typedef name
|
2017-12-02 02:41:27 +01:00
|
|
|
|
** "`iterator`" (similar to the usage within the STL). For more advanced
|
2009-07-15 07:26:49 +02:00
|
|
|
|
** usage, the providing container might want to subclass these iterators,
|
|
|
|
|
|
** e.g. to provide an additional, specialised API.
|
|
|
|
|
|
**
|
2011-11-19 18:01:48 +01:00
|
|
|
|
** Depending on the concrete situation, several flavours are provided:
|
2009-07-15 07:26:49 +02:00
|
|
|
|
** - the IterAdapter retains an active callback connection to the
|
|
|
|
|
|
** controlling container, thus allowing arbitrary complex behaviour.
|
2012-05-19 02:13:18 +02:00
|
|
|
|
** - the IterStateWrapper uses a variation of that approach, where the
|
2024-11-26 17:35:05 +01:00
|
|
|
|
** representation of the current state is embedded as a *State Core*
|
|
|
|
|
|
** value element right into the iterator instance.
|
2023-07-13 16:34:10 +02:00
|
|
|
|
** - very similar is IterableDecorator, but this time directly as
|
|
|
|
|
|
** decorator to inherit from the »state core«, and without checks.
|
2009-07-15 07:26:49 +02:00
|
|
|
|
** - the RangeIter allows just to expose a range of elements defined
|
|
|
|
|
|
** by a STL-like pair of "start" and "end" iterators
|
2015-06-05 18:52:56 +02:00
|
|
|
|
**
|
|
|
|
|
|
** Some more specific use cases are provided in the extension header
|
|
|
|
|
|
** iter-adapter-ptr-deref.hpp
|
2009-07-15 07:26:49 +02:00
|
|
|
|
** - often, objects are managed internally by pointers, while allowing
|
|
|
|
|
|
** the clients to use direct references; to support this usage scenario,
|
|
|
|
|
|
** PtrDerefIter wraps an existing iterator, while dereferencing any value
|
2011-11-19 18:01:48 +01:00
|
|
|
|
** automatically on access.
|
2013-04-08 02:03:43 +02:00
|
|
|
|
** - for some (very specific) usage situations we intend to explore the
|
|
|
|
|
|
** contents of a stable and unmodifiable data structure through pointers.
|
|
|
|
|
|
** The AddressExposingIter wraps another Lumiera Forward Iterator and
|
|
|
|
|
|
** exposes addresses -- assuming the used source iterator is exposing
|
2017-12-02 02:41:27 +01:00
|
|
|
|
** references to pre-existing storage locations (not temporaries).
|
2011-11-19 18:01:48 +01:00
|
|
|
|
**
|
2017-12-02 02:41:27 +01:00
|
|
|
|
** There are many further ways of building a Lumiera Forward Iterator.
|
2024-11-26 17:35:05 +01:00
|
|
|
|
** For example, lib::IterSource exposes an "iterable" source of data elements,
|
2011-11-19 18:01:48 +01:00
|
|
|
|
** while hiding the actual container or generator implementation behind a
|
2023-07-11 01:53:14 +02:00
|
|
|
|
** VTable call. Furthermore, complex processing chains with recursive
|
|
|
|
|
|
** expansion can be built with the \ref IterExporer builder function.
|
|
|
|
|
|
** Besides, there are adapters for the most common usages with STL
|
|
|
|
|
|
** containers, and such iterators can also be combined and
|
2017-12-02 02:41:27 +01:00
|
|
|
|
** extended with the help of \ref itertools.hpp
|
2011-11-19 18:01:48 +01:00
|
|
|
|
**
|
|
|
|
|
|
** Basically every class in compliance with our specific iterator concept
|
2012-05-19 02:13:18 +02:00
|
|
|
|
** can be used as a building block within this framework.
|
2009-07-15 07:26:49 +02:00
|
|
|
|
**
|
|
|
|
|
|
**
|
2017-12-02 02:41:27 +01:00
|
|
|
|
** # Lumiera Forward Iterator concept
|
2009-07-15 07:26:49 +02:00
|
|
|
|
**
|
|
|
|
|
|
** Similar to the STL, instead of using a common "Iterator" base class,
|
2024-11-26 17:35:05 +01:00
|
|
|
|
** we rather define a common set of functions and behaviour which can be
|
|
|
|
|
|
** expected from any such iterator. These rules are similar to STL's
|
2009-07-15 07:26:49 +02:00
|
|
|
|
** "forward iterator", with the addition of an bool check to detect
|
2011-11-19 18:01:48 +01:00
|
|
|
|
** iteration end. The latter is inspired by the \c hasNext() function
|
2024-06-11 18:23:48 +02:00
|
|
|
|
** found in many current languages supporting iterators. However, by
|
|
|
|
|
|
** inspiration from functional programming, we deliberately do not
|
2009-07-15 07:26:49 +02:00
|
|
|
|
** support the various extended iterator concepts from STL and boost
|
2011-11-19 18:01:48 +01:00
|
|
|
|
** (random access iterators, output iterators, arithmetics, difference
|
|
|
|
|
|
** between iterators and the like). According to this concept,
|
2024-11-26 17:35:05 +01:00
|
|
|
|
** _an iterator is a promise for pulling values once,_
|
2009-07-15 07:26:49 +02:00
|
|
|
|
** and nothing beyond that.
|
|
|
|
|
|
**
|
2024-11-26 17:35:05 +01:00
|
|
|
|
** Notably,
|
|
|
|
|
|
** - any Lumiera forward iterator can be in a "exhausted" (invalid) state,
|
|
|
|
|
|
** which can be checked by the bool conversion. Especially, an instance
|
2009-07-15 07:26:49 +02:00
|
|
|
|
** created by the default ctor is always fixed to that state. This
|
2024-11-26 17:35:05 +01:00
|
|
|
|
** state is final and can not be reset, meaning that any iterator
|
|
|
|
|
|
** is a disposable one-way-off object.
|
2011-11-19 18:01:48 +01:00
|
|
|
|
** - iterators are copyable and equality comparable
|
2017-12-02 02:41:27 +01:00
|
|
|
|
** - when an iterator is _not_ in the exhausted state, it may be
|
2024-11-26 17:35:05 +01:00
|
|
|
|
** _dereferenced_ to yield the _current value_.
|
|
|
|
|
|
** - usually, the _current value_ is exposed by-ref (but by-val is possible)
|
2009-07-15 07:26:49 +02:00
|
|
|
|
** - moreover, iterators may be incremented until exhaustion.
|
2009-07-11 19:23:20 +02:00
|
|
|
|
**
|
2024-06-11 18:23:48 +02:00
|
|
|
|
** Conceptually, a Lumiera Iterator represents a lazy stream of calculations
|
|
|
|
|
|
** rather than a target value considered to be »within« a container. And while
|
2024-11-26 17:35:05 +01:00
|
|
|
|
** the result is in may cases deliberately _exposed as a reference,_ in order
|
|
|
|
|
|
** to keep the door open for special-case manipulations, for the typical usage
|
|
|
|
|
|
** it is _discouraged_ to assume anything about the source, beyond the limited
|
|
|
|
|
|
** access to some transient state as exposed during active iteration. Together,
|
|
|
|
|
|
** these rules enable a _loose coupling_ to the source of data.
|
2024-06-11 18:23:48 +02:00
|
|
|
|
**
|
2010-10-06 03:58:46 +02:00
|
|
|
|
** @see iter-adapter-test.cpp
|
|
|
|
|
|
** @see itertools.hpp
|
|
|
|
|
|
** @see IterSource (completely opaque iterator)
|
2017-12-02 02:41:27 +01:00
|
|
|
|
** @see value-type-binding.hpp
|
2010-10-06 03:58:46 +02:00
|
|
|
|
**
|
2009-07-11 19:23:20 +02:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-04-01 18:03:34 +02:00
|
|
|
|
#ifndef LIB_ITER_ADAPTER_H
|
|
|
|
|
|
#define LIB_ITER_ADAPTER_H
|
2009-07-11 19:23:20 +02:00
|
|
|
|
|
|
|
|
|
|
|
2009-07-13 01:16:30 +02:00
|
|
|
|
#include "lib/error.hpp"
|
2017-12-02 02:41:27 +01:00
|
|
|
|
#include "lib/meta/value-type-binding.hpp"
|
2009-07-11 19:23:20 +02:00
|
|
|
|
|
2018-02-16 06:48:52 +01:00
|
|
|
|
#include <iterator>
|
2025-04-06 18:18:52 +02:00
|
|
|
|
#include <limits>
|
2018-02-16 06:48:52 +01:00
|
|
|
|
|
2009-07-11 19:23:20 +02:00
|
|
|
|
|
2024-11-26 17:35:05 +01:00
|
|
|
|
namespace util { // see lib/util.hpp
|
|
|
|
|
|
template<class OBJ>
|
|
|
|
|
|
OBJ* unConst (const OBJ*);
|
|
|
|
|
|
template<class OBJ>
|
|
|
|
|
|
OBJ& unConst (OBJ const&);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2009-07-11 19:23:20 +02:00
|
|
|
|
namespace lib {
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-10-05 04:12:17 +02:00
|
|
|
|
namespace { // internal helpers
|
2015-08-16 00:16:30 +02:00
|
|
|
|
inline void
|
2009-11-02 06:06:43 +01:00
|
|
|
|
_throwIterExhausted()
|
|
|
|
|
|
{
|
|
|
|
|
|
throw lumiera::error::Invalid ("Can't iterate further",
|
|
|
|
|
|
lumiera::error::LUMIERA_ERROR_ITER_EXHAUST);
|
|
|
|
|
|
}
|
2009-07-17 20:22:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-06-05 18:32:42 +02:00
|
|
|
|
/** use a given Lumiera Forward Iterator in standard "range for loops" */
|
|
|
|
|
|
#define ENABLE_USE_IN_STD_RANGE_FOR_LOOPS(ITER) \
|
|
|
|
|
|
friend ITER begin (ITER const& it){ return it; } \
|
|
|
|
|
|
friend ITER&& begin (ITER&& it) { return static_cast<ITER&&> (it); } \
|
2018-02-16 06:48:52 +01:00
|
|
|
|
friend ITER end (ITER const&) { return ITER(); } \
|
|
|
|
|
|
using iterator_category = std::input_iterator_tag; \
|
|
|
|
|
|
using difference_type = size_t;
|
2024-03-21 22:50:18 +01:00
|
|
|
|
|
|
|
|
|
|
/** define increment operator forwarding to baseclass but returning current */
|
|
|
|
|
|
#define LIFT_PARENT_INCREMENT_OPERATOR(_BASECLASS_)\
|
|
|
|
|
|
auto& \
|
|
|
|
|
|
operator++() \
|
|
|
|
|
|
{ \
|
|
|
|
|
|
_BASECLASS_::operator++(); \
|
|
|
|
|
|
return *this; \
|
|
|
|
|
|
}
|
2015-06-05 18:32:42 +02:00
|
|
|
|
|
2009-07-11 19:23:20 +02:00
|
|
|
|
|
2024-11-22 23:54:57 +01:00
|
|
|
|
namespace iter {
|
|
|
|
|
|
/** type binding helper: an iterato's actual result type */
|
|
|
|
|
|
template<class IT>
|
|
|
|
|
|
using Yield = decltype(std::declval<IT>().operator*());
|
2024-11-26 17:35:05 +01:00
|
|
|
|
|
|
|
|
|
|
/** the _result type_ yielded by a »state core« */
|
|
|
|
|
|
template<class COR>
|
|
|
|
|
|
using CoreYield = decltype(std::declval<COR>().yield());
|
|
|
|
|
|
|
2024-11-22 23:54:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-10-06 03:37:53 +02:00
|
|
|
|
|
2009-07-11 19:23:20 +02:00
|
|
|
|
/**
|
2017-12-02 02:41:27 +01:00
|
|
|
|
* Adapter for building an implementation of the »Lumiera Forward Iterator« concept.
|
2014-11-10 04:00:39 +01:00
|
|
|
|
* The "current position" is represented as an opaque element (usually a nested iterator),
|
|
|
|
|
|
* with callbacks into the controlling container instance to manage this position.
|
2009-07-15 07:26:49 +02:00
|
|
|
|
* This allows to influence and customise the iteration process to a large extent.
|
2009-07-13 01:25:24 +02:00
|
|
|
|
* Basically such an IterAdapter behaves like the similar concept from STL, but
|
|
|
|
|
|
* - it is not just a disguised pointer (meaning, it's more expensive)
|
2009-07-13 01:16:30 +02:00
|
|
|
|
* - it checks validity on every operation and may throw
|
|
|
|
|
|
* - it has a distinct back-link to the source container
|
2023-07-09 01:32:27 +02:00
|
|
|
|
* - the source container needs to support `checkPoint()` and `iterNext()` free functions.
|
2017-12-02 02:41:27 +01:00
|
|
|
|
* - we may need friendship to implement those extension points on the container
|
2009-07-13 01:16:30 +02:00
|
|
|
|
* - the end-of-iteration can be detected by bool check
|
2009-11-01 02:02:21 +01:00
|
|
|
|
* @note it is possible to "hide" a smart-ptr within the CON template parameter.
|
2009-07-13 01:16:30 +02:00
|
|
|
|
*
|
2017-12-02 02:41:27 +01:00
|
|
|
|
* @tparam POS pointer or similar mutable link to the _current value_.
|
2023-07-11 01:53:14 +02:00
|
|
|
|
* Will be `bool()` checked to detect iteration end, end else dereferenced.
|
2017-12-02 02:41:27 +01:00
|
|
|
|
* @tparam CON type of the backing container, which needs to implement two
|
|
|
|
|
|
* extension point functions for iteration control
|
|
|
|
|
|
*
|
2009-10-17 15:40:37 +02:00
|
|
|
|
* \par Stipulations
|
|
|
|
|
|
* - POS refers to the current position within the data source of this iterator.
|
|
|
|
|
|
* -# it should be default constructible
|
|
|
|
|
|
* -# it should be copy constructible
|
|
|
|
|
|
* -# when IterAdapter is supposed to be assignable, then POS should be
|
2009-10-17 21:31:03 +02:00
|
|
|
|
* -# it should provide embedded typedefs for pointer, reference and value_type,
|
2023-07-10 18:14:29 +02:00
|
|
|
|
* or alternatively resolve these types through specialisation of meta::ValueTypeBinding.
|
2009-10-17 15:40:37 +02:00
|
|
|
|
* -# it should be convertible to the pointer type it declares
|
2010-10-02 19:22:28 +02:00
|
|
|
|
* -# dereferencing should yield a type that is convertible to the reference type
|
2009-10-25 16:18:53 +01:00
|
|
|
|
* - CON points to the data source of this iterator (typically a data container type)
|
2012-05-19 02:13:18 +02:00
|
|
|
|
* We store a pointer-like backlink to invoke a special iteration control API:
|
|
|
|
|
|
* -# \c checkPoint yields true iff the source has yet more result values to yield
|
2017-12-02 02:41:27 +01:00
|
|
|
|
* -# \c iterNext advances the POS to the next element
|
|
|
|
|
|
*
|
|
|
|
|
|
* @note
|
|
|
|
|
|
* - when POS is just a pointer, we use the pointee as value type
|
|
|
|
|
|
* - but when POS is a class, we expect the usual STL style nested typedefs
|
|
|
|
|
|
* `value_type`, `reference` and `pointer`
|
2009-10-17 15:40:37 +02:00
|
|
|
|
*
|
2009-07-13 01:16:30 +02:00
|
|
|
|
* @see scoped-ptrvect.hpp usage example
|
2017-12-02 02:41:27 +01:00
|
|
|
|
* @see value-type-binding.hpp
|
|
|
|
|
|
* @see iter-adapter-test.cpp
|
2009-07-11 19:23:20 +02:00
|
|
|
|
*/
|
2009-07-13 01:16:30 +02:00
|
|
|
|
template<class POS, class CON>
|
|
|
|
|
|
class IterAdapter
|
2009-07-11 19:23:20 +02:00
|
|
|
|
{
|
2009-10-25 16:18:53 +01:00
|
|
|
|
CON source_;
|
2009-07-14 06:29:04 +02:00
|
|
|
|
mutable POS pos_;
|
2009-07-13 01:16:30 +02:00
|
|
|
|
|
2023-05-21 02:15:02 +02:00
|
|
|
|
using _ValTrait = meta::ValueTypeBinding<std::remove_pointer_t<POS>>;
|
2017-12-02 02:41:27 +01:00
|
|
|
|
|
2009-07-13 01:16:30 +02:00
|
|
|
|
public:
|
2025-07-05 20:08:18 +02:00
|
|
|
|
using value_type = _ValTrait::value_type;
|
|
|
|
|
|
using reference = _ValTrait::reference;
|
|
|
|
|
|
using pointer = _ValTrait::pointer;
|
2017-12-02 02:41:27 +01:00
|
|
|
|
|
2009-07-13 01:16:30 +02:00
|
|
|
|
|
2009-10-25 16:18:53 +01:00
|
|
|
|
IterAdapter (CON src, POS const& startpos)
|
2009-07-13 01:16:30 +02:00
|
|
|
|
: source_(src)
|
|
|
|
|
|
, pos_(startpos)
|
2017-12-02 02:41:27 +01:00
|
|
|
|
{
|
2012-05-19 02:13:18 +02:00
|
|
|
|
check();
|
2009-07-14 06:29:04 +02:00
|
|
|
|
}
|
2009-07-13 01:16:30 +02:00
|
|
|
|
|
|
|
|
|
|
IterAdapter ()
|
2009-11-02 07:37:15 +01:00
|
|
|
|
: source_()
|
2009-10-17 15:40:37 +02:00
|
|
|
|
, pos_()
|
2009-07-13 01:16:30 +02:00
|
|
|
|
{ }
|
|
|
|
|
|
|
2018-09-08 16:37:42 +02:00
|
|
|
|
explicit
|
|
|
|
|
|
operator bool() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return isValid();
|
|
|
|
|
|
}
|
2017-04-02 06:33:56 +02:00
|
|
|
|
|
2009-07-13 01:16:30 +02:00
|
|
|
|
|
|
|
|
|
|
/* === lumiera forward iterator concept === */
|
|
|
|
|
|
|
|
|
|
|
|
reference
|
|
|
|
|
|
operator*() const
|
|
|
|
|
|
{
|
|
|
|
|
|
_maybe_throw();
|
|
|
|
|
|
return *pos_;
|
|
|
|
|
|
}
|
2009-07-14 06:29:04 +02:00
|
|
|
|
|
2009-07-13 01:16:30 +02:00
|
|
|
|
pointer
|
|
|
|
|
|
operator->() const
|
|
|
|
|
|
{
|
|
|
|
|
|
_maybe_throw();
|
2015-09-11 18:32:08 +02:00
|
|
|
|
return & *pos_;
|
2009-07-13 01:16:30 +02:00
|
|
|
|
}
|
2009-07-14 06:29:04 +02:00
|
|
|
|
|
2009-07-13 01:16:30 +02:00
|
|
|
|
IterAdapter&
|
|
|
|
|
|
operator++()
|
|
|
|
|
|
{
|
|
|
|
|
|
_maybe_throw();
|
2009-07-15 07:26:49 +02:00
|
|
|
|
iterate();
|
2009-07-13 01:16:30 +02:00
|
|
|
|
return *this;
|
|
|
|
|
|
}
|
2009-07-14 06:29:04 +02:00
|
|
|
|
|
2009-07-13 01:16:30 +02:00
|
|
|
|
bool
|
|
|
|
|
|
isValid () const
|
|
|
|
|
|
{
|
2012-05-19 02:13:18 +02:00
|
|
|
|
return check();
|
2009-07-13 01:16:30 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2009-07-14 06:29:04 +02:00
|
|
|
|
bool
|
|
|
|
|
|
empty () const
|
|
|
|
|
|
{
|
2015-09-25 02:38:59 +02:00
|
|
|
|
return not isValid();
|
2009-07-14 06:29:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-07-15 07:26:49 +02:00
|
|
|
|
protected: /* === iteration control interface === */
|
|
|
|
|
|
|
|
|
|
|
|
/** ask the controlling container if this position is valid.
|
|
|
|
|
|
* @note this function is called before any operation,
|
|
|
|
|
|
* thus the container may adjust the position value,
|
|
|
|
|
|
* for example setting it to a "stop iteration" mark.
|
2017-12-02 02:41:27 +01:00
|
|
|
|
*/
|
2009-07-15 07:26:49 +02:00
|
|
|
|
bool
|
2012-05-19 02:13:18 +02:00
|
|
|
|
check() const
|
|
|
|
|
|
{
|
2025-06-07 23:59:57 +02:00
|
|
|
|
return source_ and checkPoint (source_,pos_); // extension point: free function checkPoint(...)
|
2012-05-19 02:13:18 +02:00
|
|
|
|
}
|
2009-07-15 07:26:49 +02:00
|
|
|
|
|
|
|
|
|
|
/** ask the controlling container to yield the next position.
|
|
|
|
|
|
* The call is dispatched only if the current position is valid;
|
2012-05-19 02:13:18 +02:00
|
|
|
|
* any new position reached will typically be validated prior
|
|
|
|
|
|
* to any further access, through invocation of #check.
|
2009-07-15 07:26:49 +02:00
|
|
|
|
*/
|
2012-05-19 02:13:18 +02:00
|
|
|
|
void
|
|
|
|
|
|
iterate()
|
|
|
|
|
|
{
|
2017-10-01 03:25:33 +02:00
|
|
|
|
iterNext (source_,pos_); // extension point: free function iterNext(...)
|
2017-08-06 16:58:22 +02:00
|
|
|
|
check();
|
|
|
|
|
|
} // checkPoint() might mark end condition
|
|
|
|
|
|
// for comparison with IterAdapter{}
|
2009-07-15 07:26:49 +02:00
|
|
|
|
|
|
|
|
|
|
|
2017-08-11 16:56:18 +02:00
|
|
|
|
|
|
|
|
|
|
protected:
|
2025-07-05 20:08:18 +02:00
|
|
|
|
using ConRef = meta::RefTraits<CON>::Reference;
|
2017-08-11 16:56:18 +02:00
|
|
|
|
|
2017-08-11 21:27:51 +02:00
|
|
|
|
/** allow derived classes to access backing container */
|
|
|
|
|
|
ConRef source() { return source_; }
|
|
|
|
|
|
const ConRef source() const { return unConst(this)->source_; }
|
2017-08-11 16:56:18 +02:00
|
|
|
|
|
2017-12-09 04:30:17 +01:00
|
|
|
|
void
|
2017-12-09 05:08:24 +01:00
|
|
|
|
resetPos (POS otherPos) ////////////////////////////////////////////TICKET #1125 : get rid of this function! it should not be there; rectify IterSource!
|
2017-12-09 04:30:17 +01:00
|
|
|
|
{
|
|
|
|
|
|
pos_ = otherPos;
|
|
|
|
|
|
check();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2009-07-13 01:16:30 +02:00
|
|
|
|
private:
|
|
|
|
|
|
void
|
|
|
|
|
|
_maybe_throw() const
|
|
|
|
|
|
{
|
2015-09-25 02:38:59 +02:00
|
|
|
|
if (not isValid())
|
2009-11-02 06:06:43 +01:00
|
|
|
|
_throwIterExhausted();
|
2009-07-13 01:16:30 +02:00
|
|
|
|
}
|
2009-07-14 06:29:04 +02:00
|
|
|
|
|
2015-06-05 18:32:42 +02:00
|
|
|
|
|
2018-02-16 06:48:52 +01:00
|
|
|
|
public:
|
2015-06-05 18:32:42 +02:00
|
|
|
|
ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (IterAdapter);
|
|
|
|
|
|
|
2009-07-14 06:29:04 +02:00
|
|
|
|
/// comparison is allowed to access impl iterator
|
|
|
|
|
|
template<class P1, class P2, class CX>
|
|
|
|
|
|
friend bool operator== (IterAdapter<P1,CX> const&, IterAdapter<P2,CX> const&);
|
2009-07-11 19:23:20 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-07-14 06:29:04 +02:00
|
|
|
|
/// Supporting equality comparisons...
|
|
|
|
|
|
template<class P1, class P2, class CON>
|
2016-02-04 22:52:01 +01:00
|
|
|
|
inline bool operator== (IterAdapter<P1,CON> const& il, IterAdapter<P2,CON> const& ir) { return il.pos_ == ir.pos_; }
|
2009-07-14 06:29:04 +02:00
|
|
|
|
|
|
|
|
|
|
template<class P1, class P2, class CON>
|
2023-07-09 01:32:27 +02:00
|
|
|
|
inline bool operator!= (IterAdapter<P1,CON> const& il, IterAdapter<P2,CON> const& ir) { return not (il == ir); }
|
2009-07-14 06:29:04 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-05-16 01:01:20 +02:00
|
|
|
|
|
|
|
|
|
|
/**
|
2017-12-02 02:41:27 +01:00
|
|
|
|
* Another Lumiera Forward Iterator building block, based on incorporating a state type
|
2024-11-26 17:35:05 +01:00
|
|
|
|
* as »*State Core*«, right into the iterator. Contrast this to IterAdapter, which refers to
|
|
|
|
|
|
* a managing container behind the scenes. To the contrary, here all of the state is assumed
|
|
|
|
|
|
* to live in the custom type embedded into this iterator, accessed and manipulated through
|
2024-06-11 18:23:48 +02:00
|
|
|
|
* a dedicated _iteration control API_ exposed as member functions.
|
2012-05-16 01:01:20 +02:00
|
|
|
|
*
|
2024-11-26 17:35:05 +01:00
|
|
|
|
* # Requirements for a »State Core«
|
|
|
|
|
|
* When building iterators with the help of IterStateWrapper or \ref IterableAdapter,
|
|
|
|
|
|
* it is assumed that the adapted _state core_ type represents a process of state evolution,
|
|
|
|
|
|
* which reaches a well defined end state eventually, but this end state is also the _bottom_
|
|
|
|
|
|
* - the core is default constructible
|
|
|
|
|
|
* - this default state represents the _bottom_ (final, invalid) state.
|
|
|
|
|
|
* - copyable, because iterators are passed by value; ideally also assignable
|
2017-12-05 03:28:00 +01:00
|
|
|
|
* - this type needs to provide an *iteration control API* with the following operations
|
2024-11-26 17:35:05 +01:00
|
|
|
|
* -# \c checkPoint establishes if the given state element represents a valid active state
|
2012-05-19 02:13:18 +02:00
|
|
|
|
* -# \c iterNext evolves this state by one step (sideeffect)
|
2024-11-26 17:35:05 +01:00
|
|
|
|
* -# \c yield realises the given state, yielding an element of _result type_ \a T
|
2023-06-01 04:09:29 +02:00
|
|
|
|
* @tparam ST type of the »state core«, defaults to T.
|
2017-11-27 05:56:03 +01:00
|
|
|
|
* The resulting iterator will hold an instance of ST, which thus
|
|
|
|
|
|
* needs to be copyable and default constructible to the extent
|
|
|
|
|
|
* this is required for the iterator as such.
|
2024-11-26 22:15:33 +01:00
|
|
|
|
* @tparam T (optional) result type, usually deduced from ST::yield
|
2023-07-13 16:34:10 +02:00
|
|
|
|
* @see IterableDecorator for variation of the same concept
|
2012-05-16 01:01:20 +02:00
|
|
|
|
* @see iter-explorer-test.hpp
|
|
|
|
|
|
* @see iter-adaptor-test.cpp
|
|
|
|
|
|
*/
|
2024-11-26 22:15:33 +01:00
|
|
|
|
template<class ST, typename T =iter::CoreYield<ST>>
|
2012-05-16 01:01:20 +02:00
|
|
|
|
class IterStateWrapper
|
|
|
|
|
|
{
|
|
|
|
|
|
ST core_;
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
2025-07-05 20:08:18 +02:00
|
|
|
|
using value_type = meta::RefTraits<T>::Value;
|
|
|
|
|
|
using reference = meta::RefTraits<T>::Reference;
|
|
|
|
|
|
using pointer = meta::RefTraits<T>::Pointer;
|
2012-05-16 01:01:20 +02:00
|
|
|
|
|
2015-12-06 02:28:47 +01:00
|
|
|
|
IterStateWrapper (ST&& initialState)
|
|
|
|
|
|
: core_(std::forward<ST>(initialState))
|
2017-11-27 05:56:03 +01:00
|
|
|
|
{ }
|
2015-12-06 02:28:47 +01:00
|
|
|
|
|
2012-06-03 04:09:01 +02:00
|
|
|
|
IterStateWrapper (ST const& initialState)
|
2012-05-16 01:01:20 +02:00
|
|
|
|
: core_(initialState)
|
2017-11-27 05:56:03 +01:00
|
|
|
|
{ }
|
2012-05-16 01:01:20 +02:00
|
|
|
|
|
|
|
|
|
|
IterStateWrapper ()
|
|
|
|
|
|
: core_()
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
2018-09-08 16:37:42 +02:00
|
|
|
|
explicit
|
|
|
|
|
|
operator bool() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return isValid();
|
|
|
|
|
|
}
|
2017-04-02 06:33:56 +02:00
|
|
|
|
|
2012-05-16 01:01:20 +02:00
|
|
|
|
|
|
|
|
|
|
/* === lumiera forward iterator concept === */
|
|
|
|
|
|
|
2024-11-26 22:15:33 +01:00
|
|
|
|
T
|
2012-05-16 01:01:20 +02:00
|
|
|
|
operator*() const
|
|
|
|
|
|
{
|
2012-06-04 01:08:58 +02:00
|
|
|
|
__throw_if_empty();
|
2017-12-05 03:28:00 +01:00
|
|
|
|
return core_.yield(); // core interface: yield
|
2012-05-16 01:01:20 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pointer
|
|
|
|
|
|
operator->() const
|
|
|
|
|
|
{
|
2024-11-26 22:15:33 +01:00
|
|
|
|
if constexpr (meta::isLRef_v<T>)
|
|
|
|
|
|
{
|
|
|
|
|
|
__throw_if_empty();
|
|
|
|
|
|
return & core_.yield(); // core interface: yield
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
static_assert (!sizeof(T),
|
|
|
|
|
|
"can not provide operator-> "
|
|
|
|
|
|
"since iterator pipeline generates a value");
|
2012-05-16 01:01:20 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
IterStateWrapper&
|
|
|
|
|
|
operator++()
|
|
|
|
|
|
{
|
2012-06-04 01:08:58 +02:00
|
|
|
|
__throw_if_empty();
|
2017-12-05 03:28:00 +01:00
|
|
|
|
core_.iterNext(); // core interface: iterNext
|
2012-05-16 01:01:20 +02:00
|
|
|
|
return *this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
isValid () const
|
|
|
|
|
|
{
|
2017-12-05 03:28:00 +01:00
|
|
|
|
return core_.checkPoint(); // core interface: checkPoint
|
2012-05-16 01:01:20 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
empty () const
|
|
|
|
|
|
{
|
2015-09-25 02:38:59 +02:00
|
|
|
|
return not isValid();
|
2012-05-16 01:01:20 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-06-04 01:08:58 +02:00
|
|
|
|
protected:
|
|
|
|
|
|
|
|
|
|
|
|
/** allow derived classes to
|
|
|
|
|
|
* access state representation */
|
2024-03-21 19:57:05 +01:00
|
|
|
|
ST & stateCore() { return core_; }
|
|
|
|
|
|
ST const& stateCore() const { return core_; }
|
2012-05-16 01:01:20 +02:00
|
|
|
|
|
|
|
|
|
|
void
|
2012-06-04 01:08:58 +02:00
|
|
|
|
__throw_if_empty() const
|
2012-05-16 01:01:20 +02:00
|
|
|
|
{
|
2015-09-25 02:38:59 +02:00
|
|
|
|
if (not isValid())
|
2012-05-16 01:01:20 +02:00
|
|
|
|
_throwIterExhausted();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-05-19 02:13:18 +02:00
|
|
|
|
|
2015-06-05 18:32:42 +02:00
|
|
|
|
|
2018-02-16 06:48:52 +01:00
|
|
|
|
public:
|
2015-06-05 18:32:42 +02:00
|
|
|
|
ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (IterStateWrapper);
|
|
|
|
|
|
|
2012-05-19 02:13:18 +02:00
|
|
|
|
/// comparison is allowed to access state implementation core
|
2024-11-26 22:15:33 +01:00
|
|
|
|
template<class STX, class T1, class T2>
|
|
|
|
|
|
friend bool operator== (IterStateWrapper<STX,T1> const&, IterStateWrapper<STX,T2> const&);
|
2012-05-16 01:01:20 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-05-19 02:13:18 +02:00
|
|
|
|
/// Supporting equality comparisons of equivalent iterators (same state type)...
|
2024-11-26 22:15:33 +01:00
|
|
|
|
template<class ST, class T1, class T2>
|
2016-02-04 22:52:01 +01:00
|
|
|
|
inline bool
|
2024-11-26 22:15:33 +01:00
|
|
|
|
operator== (IterStateWrapper<ST,T1> const& il, IterStateWrapper<ST,T2> const& ir)
|
2012-05-19 02:13:18 +02:00
|
|
|
|
{
|
2017-08-13 00:07:38 +02:00
|
|
|
|
return (il.empty() and ir.empty())
|
|
|
|
|
|
or (il.isValid() and ir.isValid() and il.core_ == ir.core_);
|
2012-05-19 02:13:18 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-26 22:15:33 +01:00
|
|
|
|
template<class ST, class T1, class T2>
|
2016-02-04 22:52:01 +01:00
|
|
|
|
inline bool
|
2024-11-26 22:15:33 +01:00
|
|
|
|
operator!= (IterStateWrapper<ST,T1> const& il, IterStateWrapper<ST,T2> const& ir)
|
2017-12-02 02:41:27 +01:00
|
|
|
|
{
|
2015-09-25 02:38:59 +02:00
|
|
|
|
return not (il == ir);
|
2012-05-19 02:13:18 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-06-01 04:09:29 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* Adapter to dress up an existing »Lumiera Forward Iterator« as »state core«.
|
|
|
|
|
|
* This building block achieves the complement of \ref IterStateWrapper by providing
|
|
|
|
|
|
* the API functions expected by the latter's _state protocol;_ a combination of
|
|
|
|
|
|
* IterStateCore and IterStateWrapper layered on top behaves identical to the
|
|
|
|
|
|
* original iterator. This can be used to change some aspects of the behaviour.
|
|
|
|
|
|
* @remark directly layered by inheritance, thus public functions of the
|
|
|
|
|
|
* wrapped iterator remain visible (contrary to IterStateWrapper)
|
|
|
|
|
|
*/
|
|
|
|
|
|
template<class IT>
|
|
|
|
|
|
class IterStateCore
|
2023-07-13 14:57:28 +02:00
|
|
|
|
: public IT
|
|
|
|
|
|
{
|
|
|
|
|
|
static_assert (lib::meta::can_IterForEach<IT>::value
|
|
|
|
|
|
,"Lumiera Iterator required as source");
|
|
|
|
|
|
protected:
|
|
|
|
|
|
IT&
|
|
|
|
|
|
srcIter() const
|
|
|
|
|
|
{
|
2024-11-26 17:35:05 +01:00
|
|
|
|
return util::unConst(*this);
|
2023-07-13 14:57:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
using IT::IT;
|
|
|
|
|
|
|
|
|
|
|
|
/* === state protocol API for IterStateWrapper === */
|
|
|
|
|
|
bool
|
|
|
|
|
|
checkPoint() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return bool(srcIter());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-05 20:08:18 +02:00
|
|
|
|
IT::reference
|
2023-07-13 14:57:28 +02:00
|
|
|
|
yield() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return *srcIter();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
iterNext()
|
|
|
|
|
|
{
|
|
|
|
|
|
++ srcIter();
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-07-13 16:34:10 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* Adapter to add sanity checks to a »state core«.
|
|
|
|
|
|
* @remark It is recommended to perform this kind of sanity checking by default,
|
|
|
|
|
|
* since the performance overhead is minute compared even to a virtual function
|
|
|
|
|
|
* call. However, there might be high-performance usage scenarios, where it is
|
|
|
|
|
|
* essential for the optimiser to be able to "strip every wart".
|
|
|
|
|
|
*/
|
|
|
|
|
|
template<class COR>
|
|
|
|
|
|
class CheckedCore
|
|
|
|
|
|
: public COR
|
|
|
|
|
|
{
|
|
|
|
|
|
static_assert (lib::meta::is_StateCore<COR>::value
|
|
|
|
|
|
,"Adapted type must expose a »state core« API");
|
|
|
|
|
|
protected:
|
|
|
|
|
|
COR&
|
|
|
|
|
|
_rawCore() const
|
|
|
|
|
|
{
|
2024-11-26 17:35:05 +01:00
|
|
|
|
return util::unConst(*this);
|
2023-07-13 16:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
__throw_if_empty() const
|
|
|
|
|
|
{
|
|
|
|
|
|
if (not checkPoint())
|
|
|
|
|
|
_throwIterExhausted();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
/** blindly pass-down any argument...
|
|
|
|
|
|
* @remark allows slicing move-initialisation from decorated
|
|
|
|
|
|
*/
|
|
|
|
|
|
template<typename...ARGS>
|
|
|
|
|
|
CheckedCore (ARGS&& ...init)
|
|
|
|
|
|
: COR(std::forward<ARGS>(init)...)
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
CheckedCore() =default;
|
|
|
|
|
|
CheckedCore (CheckedCore&&) =default;
|
|
|
|
|
|
CheckedCore (CheckedCore const&) =default;
|
|
|
|
|
|
CheckedCore& operator= (CheckedCore&&) =default;
|
|
|
|
|
|
CheckedCore& operator= (CheckedCore const&) =default;
|
|
|
|
|
|
|
2024-11-24 19:53:07 +01:00
|
|
|
|
using TAG_CheckedCore_Raw = COR; ///< marker to allow unwrapping the raw core
|
|
|
|
|
|
|
2023-07-13 16:34:10 +02:00
|
|
|
|
|
|
|
|
|
|
/* === state protocol API for IterStateWrapper === */
|
|
|
|
|
|
bool
|
|
|
|
|
|
checkPoint() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return _rawCore().checkPoint();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
decltype(auto)
|
|
|
|
|
|
yield() const
|
|
|
|
|
|
{
|
|
|
|
|
|
__throw_if_empty();
|
|
|
|
|
|
return _rawCore().yield();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
iterNext()
|
|
|
|
|
|
{
|
|
|
|
|
|
__throw_if_empty();
|
|
|
|
|
|
_rawCore().iterNext();
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-11-04 23:56:16 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* Adapter to »piggy-back« a STL iterable container inline and expose it as »state core«.
|
|
|
|
|
|
* @warning be sure to understand the implications of this setup
|
|
|
|
|
|
* - when initialised by reference, the container's contents will be copied
|
|
|
|
|
|
* - when move-initialised, the container will be destroyed with this iterator
|
|
|
|
|
|
* - the container API remains visible (baseclass), which could confuse trait detection
|
|
|
|
|
|
*/
|
|
|
|
|
|
template<class CON>
|
|
|
|
|
|
class ContainerCore
|
|
|
|
|
|
: public CON
|
|
|
|
|
|
{
|
2025-07-05 20:08:18 +02:00
|
|
|
|
using Iter = CON::iterator;
|
2024-11-04 23:56:16 +01:00
|
|
|
|
|
|
|
|
|
|
Iter p_;
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
ContainerCore (CON&& container)
|
|
|
|
|
|
: CON(std::forward<CON>(container))
|
|
|
|
|
|
, p_{CON::begin()}
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
// copy and assignment acceptable (warning!)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* === »state core« protocol API === */
|
|
|
|
|
|
bool
|
|
|
|
|
|
checkPoint() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return p_ != CON::end();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
decltype(auto)
|
|
|
|
|
|
yield() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return *p_;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
iterNext()
|
|
|
|
|
|
{
|
|
|
|
|
|
++p_;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-07-13 14:57:28 +02:00
|
|
|
|
|
|
|
|
|
|
/**
|
2024-11-26 17:35:05 +01:00
|
|
|
|
* Decorator-Adapter to make a »*State Core*« iterable as Lumiera Forward Iterator.
|
2023-07-13 14:57:28 +02:00
|
|
|
|
* This is a fundamental (and low-level) building block and works essentially the
|
|
|
|
|
|
* same as IterStateWrapper — with the significant difference however that the
|
|
|
|
|
|
* _Core is mixed in by inheritance_ and thus its full interface remains publicly
|
|
|
|
|
|
* accessible. Another notable difference is that this adapter deliberately
|
|
|
|
|
|
* *performs no sanity-checks*. This can be dangerous, but allows to use this
|
|
|
|
|
|
* setup even in performance critical code.
|
2024-11-26 17:35:05 +01:00
|
|
|
|
* @warning be sure to understand the consequences of _using_ ´core.yield()´ without
|
|
|
|
|
|
* checks; an iterator built this way _must be checked_ before each use, unless
|
|
|
|
|
|
* it is guaranteed to be valid (by contextual knowledge). It might be a good idea
|
|
|
|
|
|
* to build some safety checks into the Core API functions instead, maybe even just
|
|
|
|
|
|
* as assertions, or to wrap the Core into \ref CheckedCore for most usages.
|
|
|
|
|
|
* @tparam COR type of the »state core«. The resulting iterator will _mix-in_ this type,
|
|
|
|
|
|
* and thus inherit properties like copy, move, compare, VTable, „POD-ness“.
|
2023-07-13 14:57:28 +02:00
|
|
|
|
* The COR must implement the following _iteration control API:_
|
|
|
|
|
|
* -# `checkPoint` establishes if the given state element represents a valid state
|
|
|
|
|
|
* -# ´iterNext` evolves this state by one step (sideeffect)
|
2024-11-26 22:15:33 +01:00
|
|
|
|
* -# `yield` realises the given state, exposing a result reference or value
|
2024-11-26 17:35:05 +01:00
|
|
|
|
* Furthermore, COR must be default-constructible in _disabled_ state
|
2024-11-26 02:56:28 +01:00
|
|
|
|
* @note the resulting iterator will attempt to yield a reference to type \a T when possible;
|
2024-11-26 17:35:05 +01:00
|
|
|
|
* but when the wrapped `COR::yield()` produces a value, this is passed as such, moreover
|
|
|
|
|
|
* #operator-> becomes disabled then, to prevent taking the address of the (temporary) value!
|
2023-07-13 16:34:10 +02:00
|
|
|
|
* @see IterExplorer a pipeline builder framework on top of IterableDecorator
|
|
|
|
|
|
* @see iter-explorer-test.hpp
|
|
|
|
|
|
* @see iter-adaptor-test.cpp
|
2023-07-13 14:57:28 +02:00
|
|
|
|
*/
|
2024-11-26 22:15:33 +01:00
|
|
|
|
template<class COR>
|
2023-07-13 14:57:28 +02:00
|
|
|
|
class IterableDecorator
|
|
|
|
|
|
: public COR
|
|
|
|
|
|
{
|
|
|
|
|
|
COR & _core() { return static_cast<COR&> (*this); }
|
|
|
|
|
|
COR const& _core() const { return static_cast<COR const&> (*this); }
|
|
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
|
void
|
|
|
|
|
|
__throw_if_empty() const
|
|
|
|
|
|
{
|
|
|
|
|
|
if (not isValid())
|
|
|
|
|
|
_throwIterExhausted();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
2024-11-26 22:15:33 +01:00
|
|
|
|
using YieldRes = iter::CoreYield<COR>;
|
2025-07-05 20:08:18 +02:00
|
|
|
|
using value_type = meta::RefTraits<YieldRes>::Value;
|
|
|
|
|
|
using reference = meta::RefTraits<YieldRes>::Reference;
|
|
|
|
|
|
using pointer = meta::RefTraits<YieldRes>::Pointer;
|
2024-11-26 02:56:28 +01:00
|
|
|
|
|
2023-07-13 14:57:28 +02:00
|
|
|
|
|
|
|
|
|
|
/** by default, pass anything down for initialisation of the core.
|
|
|
|
|
|
* @note especially this allows move-initialisation from an existing core.
|
|
|
|
|
|
* @remarks to prevent this rule from "eating" the standard copy operations,
|
|
|
|
|
|
* and the no-op default ctor, we need to declare them explicitly below.
|
|
|
|
|
|
*/
|
|
|
|
|
|
template<typename...ARGS>
|
|
|
|
|
|
IterableDecorator (ARGS&& ...init)
|
|
|
|
|
|
: COR(std::forward<ARGS>(init)...)
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
IterableDecorator() =default;
|
|
|
|
|
|
IterableDecorator (IterableDecorator&&) =default;
|
|
|
|
|
|
IterableDecorator (IterableDecorator const&) =default;
|
|
|
|
|
|
IterableDecorator& operator= (IterableDecorator&&) =default;
|
|
|
|
|
|
IterableDecorator& operator= (IterableDecorator const&) =default;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* === lumiera forward iterator concept === */
|
|
|
|
|
|
|
|
|
|
|
|
explicit operator bool() const { return isValid(); }
|
|
|
|
|
|
|
2024-11-26 02:56:28 +01:00
|
|
|
|
YieldRes
|
2023-07-13 14:57:28 +02:00
|
|
|
|
operator*() const
|
|
|
|
|
|
{
|
2024-11-26 17:35:05 +01:00
|
|
|
|
return _core().yield(); // core interface: yield
|
2023-07-13 14:57:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pointer
|
|
|
|
|
|
operator->() const
|
|
|
|
|
|
{
|
2024-11-26 22:15:33 +01:00
|
|
|
|
if constexpr (meta::isLRef_v<YieldRes>)
|
2024-11-26 17:35:05 +01:00
|
|
|
|
return & _core().yield(); // core interface: yield
|
2024-11-26 02:56:28 +01:00
|
|
|
|
else
|
2024-11-26 22:15:33 +01:00
|
|
|
|
static_assert (!sizeof(COR),
|
2024-11-26 02:56:28 +01:00
|
|
|
|
"can not provide operator-> "
|
|
|
|
|
|
"since iterator pipeline generates a value");
|
2023-07-13 14:57:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
IterableDecorator&
|
|
|
|
|
|
operator++()
|
|
|
|
|
|
{
|
2024-11-26 17:35:05 +01:00
|
|
|
|
_core().iterNext(); // core interface: iterNext
|
2023-07-13 14:57:28 +02:00
|
|
|
|
return *this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
isValid () const
|
|
|
|
|
|
{
|
2024-11-26 17:35:05 +01:00
|
|
|
|
return _core().checkPoint(); // core interface: checkPoint
|
2023-07-13 14:57:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
empty () const
|
|
|
|
|
|
{
|
|
|
|
|
|
return not isValid();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (IterableDecorator);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Supporting equality comparisons of equivalent iterators (equivalent state core)...
|
|
|
|
|
|
friend bool
|
2024-11-26 22:15:33 +01:00
|
|
|
|
operator== (IterableDecorator const& il, IterableDecorator const& ir)
|
2023-06-01 04:09:29 +02:00
|
|
|
|
{
|
2023-07-13 14:57:28 +02:00
|
|
|
|
return (il.empty() and ir.empty())
|
|
|
|
|
|
or (il.isValid() and ir.isValid() and il._core() == ir._core());
|
|
|
|
|
|
}
|
|
|
|
|
|
friend bool
|
2024-11-26 22:15:33 +01:00
|
|
|
|
operator!= (IterableDecorator const& il, IterableDecorator const& ir)
|
2023-07-13 14:57:28 +02:00
|
|
|
|
{
|
|
|
|
|
|
return not (il == ir);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
2023-06-01 04:09:29 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-05-19 02:13:18 +02:00
|
|
|
|
|
2009-07-14 06:29:04 +02:00
|
|
|
|
|
|
|
|
|
|
|
2009-07-13 01:16:30 +02:00
|
|
|
|
|
2017-12-02 02:41:27 +01:00
|
|
|
|
/**
|
Library: a subtle fix to iterator value-type deduction
After some investigation, I seem to understand the problem better now.
This builds upon the Introduction of our `ValueTraits´ two years ago:
e176e540043177
The root cause to these ongoing sporadic problems with type deduction
seems to be that we encounter a **clash of concepts**: What Lumiera calls
an »Iterator« is essentially different to the order of terms used by the STL.
In the STL, an iterator can be seen as an ''abstracted pointer'', and is
thus only meaningful in conjunction with a backing container. It is also
this backing STL container that provides the value-type definitions.
In Lumiera however, an ''Iterator is a promise to retrieve a sequence of evaluations''.
For that reason, the Iterator itself provides value-type definitions,
and the notion of "iterable" can largely be conflated with "Lumiera Iterator".
Now, the `lib::RangeIter` can act as a bridge between both usage patterns,
and thus it is possible to create a `RangeIter` both from a Lumiera Iterator
or from a STL container. (As an aside, this is a similar idea as promoted
by the "Range TS" in C++20 -- yet the Lumiera iterator concept is much more
abstract and allows evaluations like tree expansion and search algorithms,
which would be never possible with the STL based notion of iteration)...
Thus the value type binding in `RangeIter` is the only correct place
where to mitigate this ''clash of concepts:''
* if the `IT` parameter is a Lumiera iterator, we need to retrieve
the type bindings defined therein
* otherwise, we can assume that `IT` is an STL iterator and thus
essentially a pointer, and we must strip that pointer and work from there.
2026-03-25 21:07:26 +01:00
|
|
|
|
* Access a STL element range through a »Lumiera Forward Iterator«.
|
2009-07-15 07:26:49 +02:00
|
|
|
|
* An instance of this iterator adapter is completely self-contained
|
2017-12-02 02:41:27 +01:00
|
|
|
|
* and allows to iterate once over the range of elements, until
|
|
|
|
|
|
* `pos==end`. Thus, a custom container may expose a range of
|
2009-07-15 07:26:49 +02:00
|
|
|
|
* elements of an embedded STL container, without controlling
|
Library: a subtle fix to iterator value-type deduction
After some investigation, I seem to understand the problem better now.
This builds upon the Introduction of our `ValueTraits´ two years ago:
e176e540043177
The root cause to these ongoing sporadic problems with type deduction
seems to be that we encounter a **clash of concepts**: What Lumiera calls
an »Iterator« is essentially different to the order of terms used by the STL.
In the STL, an iterator can be seen as an ''abstracted pointer'', and is
thus only meaningful in conjunction with a backing container. It is also
this backing STL container that provides the value-type definitions.
In Lumiera however, an ''Iterator is a promise to retrieve a sequence of evaluations''.
For that reason, the Iterator itself provides value-type definitions,
and the notion of "iterable" can largely be conflated with "Lumiera Iterator".
Now, the `lib::RangeIter` can act as a bridge between both usage patterns,
and thus it is possible to create a `RangeIter` both from a Lumiera Iterator
or from a STL container. (As an aside, this is a similar idea as promoted
by the "Range TS" in C++20 -- yet the Lumiera iterator concept is much more
abstract and allows evaluations like tree expansion and search algorithms,
which would be never possible with the STL based notion of iteration)...
Thus the value type binding in `RangeIter` is the only correct place
where to mitigate this ''clash of concepts:''
* if the `IT` parameter is a Lumiera iterator, we need to retrieve
the type bindings defined therein
* otherwise, we can assume that `IT` is an STL iterator and thus
essentially a pointer, and we must strip that pointer and work from there.
2026-03-25 21:07:26 +01:00
|
|
|
|
* the details of the iteration (as would be possible using the
|
2009-07-15 07:26:49 +02:00
|
|
|
|
* more generic IterAdapter).
|
2017-12-02 02:41:27 +01:00
|
|
|
|
* @note
|
Library: a subtle fix to iterator value-type deduction
After some investigation, I seem to understand the problem better now.
This builds upon the Introduction of our `ValueTraits´ two years ago:
e176e540043177
The root cause to these ongoing sporadic problems with type deduction
seems to be that we encounter a **clash of concepts**: What Lumiera calls
an »Iterator« is essentially different to the order of terms used by the STL.
In the STL, an iterator can be seen as an ''abstracted pointer'', and is
thus only meaningful in conjunction with a backing container. It is also
this backing STL container that provides the value-type definitions.
In Lumiera however, an ''Iterator is a promise to retrieve a sequence of evaluations''.
For that reason, the Iterator itself provides value-type definitions,
and the notion of "iterable" can largely be conflated with "Lumiera Iterator".
Now, the `lib::RangeIter` can act as a bridge between both usage patterns,
and thus it is possible to create a `RangeIter` both from a Lumiera Iterator
or from a STL container. (As an aside, this is a similar idea as promoted
by the "Range TS" in C++20 -- yet the Lumiera iterator concept is much more
abstract and allows evaluations like tree expansion and search algorithms,
which would be never possible with the STL based notion of iteration)...
Thus the value type binding in `RangeIter` is the only correct place
where to mitigate this ''clash of concepts:''
* if the `IT` parameter is a Lumiera iterator, we need to retrieve
the type bindings defined therein
* otherwise, we can assume that `IT` is an STL iterator and thus
essentially a pointer, and we must strip that pointer and work from there.
2026-03-25 21:07:26 +01:00
|
|
|
|
* - when IT is a STL iterator, we use the pointee as value type
|
|
|
|
|
|
* - but when IT is a »Lumiera Forward Iterator«, we need to evaluate
|
|
|
|
|
|
* nested typedefs `value_type`, `reference` and `pointer`
|
|
|
|
|
|
* @remark the reason for this tricky distinction is a _clash of concepts:_
|
|
|
|
|
|
* In Lumiera, »Iterator« has a special meaning and can used on itself,
|
|
|
|
|
|
* while in the STL, an »Iterator« is a pointer in disguise and is
|
|
|
|
|
|
* only meaningful when the backing container is known. This adaptor,
|
|
|
|
|
|
* RangeIter, acts as a bridge and levels those usage styles.
|
2009-07-15 07:26:49 +02:00
|
|
|
|
*/
|
|
|
|
|
|
template<class IT>
|
|
|
|
|
|
class RangeIter
|
|
|
|
|
|
{
|
|
|
|
|
|
IT p_;
|
|
|
|
|
|
IT e_;
|
|
|
|
|
|
|
Library: a subtle fix to iterator value-type deduction
After some investigation, I seem to understand the problem better now.
This builds upon the Introduction of our `ValueTraits´ two years ago:
e176e540043177
The root cause to these ongoing sporadic problems with type deduction
seems to be that we encounter a **clash of concepts**: What Lumiera calls
an »Iterator« is essentially different to the order of terms used by the STL.
In the STL, an iterator can be seen as an ''abstracted pointer'', and is
thus only meaningful in conjunction with a backing container. It is also
this backing STL container that provides the value-type definitions.
In Lumiera however, an ''Iterator is a promise to retrieve a sequence of evaluations''.
For that reason, the Iterator itself provides value-type definitions,
and the notion of "iterable" can largely be conflated with "Lumiera Iterator".
Now, the `lib::RangeIter` can act as a bridge between both usage patterns,
and thus it is possible to create a `RangeIter` both from a Lumiera Iterator
or from a STL container. (As an aside, this is a similar idea as promoted
by the "Range TS" in C++20 -- yet the Lumiera iterator concept is much more
abstract and allows evaluations like tree expansion and search algorithms,
which would be never possible with the STL based notion of iteration)...
Thus the value type binding in `RangeIter` is the only correct place
where to mitigate this ''clash of concepts:''
* if the `IT` parameter is a Lumiera iterator, we need to retrieve
the type bindings defined therein
* otherwise, we can assume that `IT` is an STL iterator and thus
essentially a pointer, and we must strip that pointer and work from there.
2026-03-25 21:07:26 +01:00
|
|
|
|
static constexpr bool _is_LumieraIter = meta::can_IterForEach<IT>::value;
|
|
|
|
|
|
using _ValTrait = std::conditional_t<_is_LumieraIter, meta::ValueTypeBinding<IT>
|
|
|
|
|
|
, meta::ValueTypeBinding<meta::remove_pointer_t<IT>>>;
|
2017-12-02 02:41:27 +01:00
|
|
|
|
|
2009-07-15 07:26:49 +02:00
|
|
|
|
public:
|
2025-07-05 20:08:18 +02:00
|
|
|
|
using pointer = _ValTrait::pointer;
|
|
|
|
|
|
using reference = _ValTrait::reference;
|
2023-05-21 02:15:02 +02:00
|
|
|
|
|
|
|
|
|
|
/// @note special twist, since a STL const_iterator would yield a non-const `value_type`
|
2025-07-05 20:08:18 +02:00
|
|
|
|
using value_type = std::remove_reference<reference>::type;
|
2017-12-02 02:41:27 +01:00
|
|
|
|
|
2009-07-15 07:26:49 +02:00
|
|
|
|
|
|
|
|
|
|
RangeIter (IT const& start, IT const& end)
|
|
|
|
|
|
: p_(start)
|
|
|
|
|
|
, e_(end)
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
RangeIter ()
|
2009-11-21 04:48:29 +01:00
|
|
|
|
: p_()
|
|
|
|
|
|
, e_()
|
2009-07-15 07:26:49 +02:00
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-07-17 04:13:14 +02:00
|
|
|
|
/** allow copy,
|
|
|
|
|
|
* when the underlying iterators
|
|
|
|
|
|
* are compatible or convertible */
|
|
|
|
|
|
template<class I2>
|
|
|
|
|
|
RangeIter (I2 const& oIter)
|
|
|
|
|
|
: p_(oIter.getPos())
|
|
|
|
|
|
, e_(oIter.getEnd())
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
2018-09-08 16:37:42 +02:00
|
|
|
|
explicit
|
|
|
|
|
|
operator bool() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return isValid();
|
|
|
|
|
|
}
|
2017-04-02 06:33:56 +02:00
|
|
|
|
|
2009-07-17 04:13:14 +02:00
|
|
|
|
|
2009-07-15 07:26:49 +02:00
|
|
|
|
/* === lumiera forward iterator concept === */
|
|
|
|
|
|
|
|
|
|
|
|
reference
|
|
|
|
|
|
operator*() const
|
|
|
|
|
|
{
|
|
|
|
|
|
_maybe_throw();
|
|
|
|
|
|
return *p_;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pointer
|
|
|
|
|
|
operator->() const
|
|
|
|
|
|
{
|
|
|
|
|
|
_maybe_throw();
|
|
|
|
|
|
return &(*p_);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
RangeIter&
|
|
|
|
|
|
operator++()
|
|
|
|
|
|
{
|
2009-07-17 20:22:48 +02:00
|
|
|
|
_maybe_throw();
|
2009-07-15 07:26:49 +02:00
|
|
|
|
++p_;
|
|
|
|
|
|
return *this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
isValid () const
|
|
|
|
|
|
{
|
2025-06-07 23:59:57 +02:00
|
|
|
|
return (p_!= IT()) and (p_ != e_);
|
2009-07-15 07:26:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
empty () const
|
|
|
|
|
|
{
|
2015-09-25 02:38:59 +02:00
|
|
|
|
return not isValid();
|
2009-07-15 07:26:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2009-07-17 04:13:14 +02:00
|
|
|
|
|
|
|
|
|
|
/** access wrapped STL iterator */
|
|
|
|
|
|
const IT& getPos() const { return p_; }
|
|
|
|
|
|
const IT& getEnd() const { return e_; }
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-06-05 18:32:42 +02:00
|
|
|
|
ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (RangeIter);
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-07-15 07:26:49 +02:00
|
|
|
|
private:
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
_maybe_throw() const
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!isValid())
|
2009-11-02 06:06:43 +01:00
|
|
|
|
_throwIterExhausted();
|
2009-07-15 07:26:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Supporting equality comparisons...
|
|
|
|
|
|
template<class I1, class I2>
|
2025-06-07 23:59:57 +02:00
|
|
|
|
inline bool operator== (RangeIter<I1> const& il, RangeIter<I2> const& ir) { return (!il and !ir) or (il.getPos() == ir.getPos()); }
|
2017-12-02 02:41:27 +01:00
|
|
|
|
|
2009-07-15 07:26:49 +02:00
|
|
|
|
template<class I1, class I2>
|
2025-06-07 23:59:57 +02:00
|
|
|
|
inline bool operator!= (RangeIter<I1> const& il, RangeIter<I2> const& ir) { return not (il == ir); }
|
2009-07-15 07:26:49 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-02-04 22:01:48 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* Enumerate all "numbers" within a range.
|
|
|
|
|
|
* This allows to build pipelines based on all
|
|
|
|
|
|
* numbers "for `i` from `1...N`". This range is _half open_,
|
|
|
|
|
|
* i.e. the start is inclusive and the end point is exclusive.
|
2024-11-22 23:54:57 +01:00
|
|
|
|
* @remark default constructed iters are empty and compare equal with
|
|
|
|
|
|
* any other exhausted NumIter; essential requirement for a
|
|
|
|
|
|
* Lumiera Forward Iterator (allows use in range-for-loops).
|
2016-02-04 22:01:48 +01:00
|
|
|
|
* @tparam INT a number like type, which can be incremented and compared.
|
2024-11-22 23:54:57 +01:00
|
|
|
|
* @note deliberately yields by-value and has no `operator->`
|
2016-02-04 22:01:48 +01:00
|
|
|
|
*/
|
|
|
|
|
|
template<typename INT>
|
|
|
|
|
|
class NumIter
|
|
|
|
|
|
{
|
|
|
|
|
|
INT i_;
|
|
|
|
|
|
INT e_;
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
2025-06-07 23:59:57 +02:00
|
|
|
|
using pointer = INT*;
|
|
|
|
|
|
using reference = INT&;
|
|
|
|
|
|
using value_type = INT ;
|
2016-02-04 22:01:48 +01:00
|
|
|
|
|
|
|
|
|
|
NumIter (INT start, INT end)
|
|
|
|
|
|
: i_(start)
|
|
|
|
|
|
, e_(end)
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
template<typename X>
|
|
|
|
|
|
NumIter (X&& start, X&& end)
|
|
|
|
|
|
: i_(std::forward<X>(start))
|
|
|
|
|
|
, e_(std::forward<X>(end))
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
NumIter ()
|
|
|
|
|
|
: i_()
|
|
|
|
|
|
, e_()
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
// standard copy operations acceptable
|
|
|
|
|
|
|
2018-09-08 16:37:42 +02:00
|
|
|
|
explicit
|
|
|
|
|
|
operator bool() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return isValid();
|
|
|
|
|
|
}
|
2017-04-02 06:33:56 +02:00
|
|
|
|
|
2016-02-04 22:01:48 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* === lumiera forward iterator concept === */
|
|
|
|
|
|
|
2024-11-22 23:54:57 +01:00
|
|
|
|
value_type
|
2016-02-04 22:01:48 +01:00
|
|
|
|
operator*() const
|
|
|
|
|
|
{
|
|
|
|
|
|
_maybe_throw();
|
|
|
|
|
|
return i_;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NumIter&
|
|
|
|
|
|
operator++()
|
|
|
|
|
|
{
|
|
|
|
|
|
_maybe_throw();
|
|
|
|
|
|
++i_;
|
|
|
|
|
|
return *this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
isValid () const
|
|
|
|
|
|
{
|
Library: investigate how a »zip iterator« can be built
Basically I am sick of writing for-loops in those cases
where the actual iteration is based on one or several data sources,
and I just need some damn index counter. Nothing against for-loops
in general — they have their valid uses — sometimes a for-loop is KISS
But in these typical cases, an iterator-based solution would be a
one-liner, when also exploiting the structured bindings of C++17
''I must admit that I want this for a loooooong time —''
...but always got intimidated again when thinking through the fine points.
Basically it „should be dead simple“ — as they say
Well — — it ''is'' simple, after getting the nasty aspects of tuple binding
and reference data types out of the way. Yesterday, while writing those
`TestFrame` test cases (which are again an example where you want to iterate
over two word sequences simultaneously and just compare them), I noticed that
last year I learned about the `std::apply`-to-fold-expression trick, and
that this solution pattern could be adapted to construct a tuple directly,
thereby circumventing most of the problems related to ''perfect forwarding''
So now we have a new util function `mapEach` (defined in `tuple-helper.hpp`)
and I have learned how to make this application completely generic.
As a second step, I implemented a proof-of-concept in `IterZip_test`,
which indeed was not really challenging, because the `IterExplorer`
is so very sophisticated by now and handles most cases with transparent
type-driven adaptors. A lot of work went into `IterExplorer` over the years,
and this pays off now.
The solution works as follows:
* apply the `lib::explore()` constructor function to the varargs
* package the resulting `IterExplorer` instantiations into a tuple
* build a »state core« implementation which just lifts out the three
iterator primitives onto this ''product type'' (i.e. the tuple)
* wrap it in yet another `IterExplorer`
* add a transformer function on top to extract a value-tuple for each ''yield'
As expected, works out-of-the-box, with all conceivable variants and wild
mixes of iterators, const, pointers, references, you name it....
PS: I changed the rendering of unsigned types in diagnostic output
to use the short notation, e.g. `uint` instead of `unsigned int`.
This dramatically improves the legibility of verification strings.
2024-11-21 23:30:07 +01:00
|
|
|
|
return i_ < e_; // NOTE: use comparison to detect iteration end
|
2016-02-04 22:01:48 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
empty () const
|
|
|
|
|
|
{
|
|
|
|
|
|
return not isValid();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** access wrapped index elements */
|
2024-11-22 23:54:57 +01:00
|
|
|
|
INT& getPos() const { return i_; }
|
|
|
|
|
|
INT& getEnd() const { return e_; }
|
2016-02-04 22:01:48 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (NumIter);
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-11-22 23:54:57 +01:00
|
|
|
|
/// Supporting equality comparisons...
|
|
|
|
|
|
bool operator!= (NumIter const& o) const { return not operator==(o); }
|
2024-11-26 17:35:05 +01:00
|
|
|
|
bool operator== (NumIter const& o) const { return (empty() and o.empty()) // empty iters must be equal (Lumiera iter requirement)
|
2024-11-22 23:54:57 +01:00
|
|
|
|
or (i_ == o.i_ and e_ == o.e_); }
|
|
|
|
|
|
|
2016-02-04 22:01:48 +01:00
|
|
|
|
private:
|
|
|
|
|
|
void
|
|
|
|
|
|
_maybe_throw() const
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!isValid())
|
|
|
|
|
|
_throwIterExhausted();
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** convenience function to iterate "each number" */
|
|
|
|
|
|
template<typename INT>
|
|
|
|
|
|
inline NumIter<INT>
|
2024-11-22 19:11:53 +01:00
|
|
|
|
eachNum (INT start = std::numeric_limits<INT>::min()
|
|
|
|
|
|
,INT end = std::numeric_limits<INT>::max())
|
2016-02-04 22:01:48 +01:00
|
|
|
|
{
|
|
|
|
|
|
return NumIter<INT> (start, end);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-12-02 02:41:27 +01:00
|
|
|
|
/**
|
2009-07-17 20:22:48 +02:00
|
|
|
|
* Helper for type rewritings:
|
|
|
|
|
|
* get the element type for an iterator like entity
|
|
|
|
|
|
*/
|
|
|
|
|
|
template<class TY>
|
|
|
|
|
|
struct IterType;
|
|
|
|
|
|
|
|
|
|
|
|
template<template<class,class> class Iter, class TY, class CON>
|
2016-12-23 04:23:03 +01:00
|
|
|
|
struct IterType<Iter<TY,CON>>
|
2009-07-17 20:22:48 +02:00
|
|
|
|
{
|
2025-06-07 23:59:57 +02:00
|
|
|
|
using Container = CON;
|
|
|
|
|
|
using ElemType = TY ;
|
2009-07-17 20:22:48 +02:00
|
|
|
|
|
|
|
|
|
|
template<class T2>
|
2011-11-19 17:46:24 +01:00
|
|
|
|
struct SimilarIter ///< rebind to a similarly structured Iterator with value type T2
|
2009-07-17 20:22:48 +02:00
|
|
|
|
{
|
2025-06-07 23:59:57 +02:00
|
|
|
|
using Type = Iter<T2,CON>;
|
2009-07-17 20:22:48 +02:00
|
|
|
|
};
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
template<class IT>
|
2016-12-23 04:23:03 +01:00
|
|
|
|
struct IterType<RangeIter<IT>>
|
2009-07-17 20:22:48 +02:00
|
|
|
|
: IterType<IT>
|
|
|
|
|
|
{
|
|
|
|
|
|
template<class T2>
|
|
|
|
|
|
struct SimilarIter ///< rebind to rewritten Iterator wrapped into RangeIter
|
|
|
|
|
|
{
|
2025-07-05 20:08:18 +02:00
|
|
|
|
using WrappedIter = IterType<IT>::template SimilarIter<T2>::Type;
|
2025-06-07 23:59:57 +02:00
|
|
|
|
using Type = RangeIter<WrappedIter>;
|
2009-07-17 20:22:48 +02:00
|
|
|
|
};
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-12-02 02:41:27 +01:00
|
|
|
|
/** wrapper to expose values as const */
|
2013-04-08 02:03:43 +02:00
|
|
|
|
template<class IT>
|
|
|
|
|
|
class ConstIter
|
|
|
|
|
|
{
|
|
|
|
|
|
IT i_; ///< nested source iterator
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
2025-07-05 20:08:18 +02:00
|
|
|
|
using value_type = const IT::value_type;
|
|
|
|
|
|
using pointer = const IT::pointer ;
|
|
|
|
|
|
using reference = const IT::reference ;
|
2013-04-08 02:03:43 +02:00
|
|
|
|
|
|
|
|
|
|
ConstIter (IT srcIter)
|
|
|
|
|
|
: i_(srcIter)
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
2018-09-08 16:37:42 +02:00
|
|
|
|
explicit
|
|
|
|
|
|
operator bool() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return isValid();
|
|
|
|
|
|
}
|
2013-04-08 02:03:43 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* === lumiera forward iterator concept === */
|
|
|
|
|
|
|
|
|
|
|
|
reference
|
|
|
|
|
|
operator*() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return *i_;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pointer
|
|
|
|
|
|
operator->() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return i_.operator->();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ConstIter&
|
|
|
|
|
|
operator++()
|
|
|
|
|
|
{
|
|
|
|
|
|
++i_;
|
|
|
|
|
|
return *this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
isValid () const
|
|
|
|
|
|
{
|
|
|
|
|
|
return bool(i_);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
empty () const
|
|
|
|
|
|
{
|
2015-09-25 02:38:59 +02:00
|
|
|
|
return not isValid();
|
2013-04-08 02:03:43 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** access the wrapped implementation iterator */
|
|
|
|
|
|
IT const&
|
|
|
|
|
|
getBase() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return i_;
|
|
|
|
|
|
}
|
2018-02-16 06:48:52 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (ConstIter);
|
2013-04-08 02:03:43 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Supporting equality comparisons...
|
|
|
|
|
|
template<class I1, class I2>
|
2016-02-04 22:52:01 +01:00
|
|
|
|
inline bool operator== (ConstIter<I1> const& il, ConstIter<I2> const& ir) { return il.getBase() == ir.getBase(); }
|
2013-04-08 02:03:43 +02:00
|
|
|
|
|
|
|
|
|
|
template<class I1, class I2>
|
2016-02-04 22:52:01 +01:00
|
|
|
|
inline bool operator!= (ConstIter<I1> const& il, ConstIter<I2> const& ir) { return not (il == ir); }
|
2013-04-08 02:03:43 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-06-05 18:32:42 +02:00
|
|
|
|
}// namespace lib
|
|
|
|
|
|
#endif /*LIB_ITER_ADAPTER_H*/
|