From f716fb0bee98da0e177b8c024d0a0c8c4039f531 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 20 Mar 2024 23:58:42 +0100 Subject: [PATCH] Library: build a helper to encapsulate container access by index ...mostly we want the usual convenient handling pattern for iterators, but with the proviso actually to perform an access by subscript, and the ability to re-set to another current index --- src/lib/iter-index.hpp | 144 ++++++++++++++++ tests/library/iter-index-test.cpp | 265 ++++++++++++++++++++++++++++++ wiki/thinkPad.ichthyo.mm | 112 +++++++++++-- 3 files changed, 506 insertions(+), 15 deletions(-) create mode 100644 src/lib/iter-index.hpp create mode 100644 tests/library/iter-index-test.cpp diff --git a/src/lib/iter-index.hpp b/src/lib/iter-index.hpp new file mode 100644 index 000000000..93b058289 --- /dev/null +++ b/src/lib/iter-index.hpp @@ -0,0 +1,144 @@ +/* + ITER-INDEX.hpp - iterator with indexed random-access to referred container + + Copyright (C) Lumiera.org + 2024, Hermann Vosseler + + This program 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +/** @file iter-index.hpp + ** Iterator-style access handle to a referred container with subscript index. + ** This wrapper packages a current index number and a back-link to some data container + ** with subscript operator and range check. This allows to hand out a navigable access point + ** to a processing algorithm while abstracting away the actual data storage. Besides usage + ** as »Lumiera Forward Iterator«, the current access position can be retrieved directly + ** and it can be relocated to another valid index position; this implies also the ability + ** to re-set the iteration to the container's start. + ** + ** @see IterIndex_test + ** @see iter-adapter.hpp + ** @see [usage example](\ref lib::TextTemplate::InstanceCore) + ** + */ + + +#ifndef SRC_LIB_ITER_INDEX_H +#define SRC_LIB_ITER_INDEX_H + + +#include "lib/iter-adapter.hpp" + +//#include +//#include + + +namespace lib { + + namespace {// Implementation of the access core + + template + struct IndexAccessCore + { + PTR data_{}; + size_t idx_{0}; + + using ResVal = decltype(data_->operator[](0)); + + using value_type = typename meta::RefTraits::Value; + using reference = typename meta::RefTraits::Reference; + + using IterWrapper = lib::IterStateWrapper; + + bool + checkPoint() const + { + return isValidIDX(idx_); + } + + reference + yield() const + { + return (*data_)[idx_]; + } + + void + iterNext() + { + ++idx_; + } + + + bool + isValidIDX (size_t idx) const + { + return bool(data_) + and idx < data_->size(); + } + + + friend bool operator== (IndexAccessCore const& c1, IndexAccessCore const& c2) + { + return c1.data_ == c2.data_ and (not c1.data_ or c1.idx_ == c2.idx_); + } + friend bool operator!= (IndexAccessCore const& c1, IndexAccessCore const& c2) + { + return not (c1 == c2); + } + }; + + }//(End)Implementation + + + /** + * + */ + template + class IterIndex + : public IndexAccessCore::IterWrapper + { + using _Cor = IndexAccessCore; + using _Par = typename _Cor::IterWrapper; + + public: + IterIndex() = default; + IterIndex (CON& dataContainer) + : _Par{_Cor{&dataContainer, 0}} + { } + + + size_t + getIDX() const + { + _Par::__throw_if_empty(); + return const_cast(this)->stateCore().idx_; + } + + void + setIDX (size_t newIDX) + { + auto& core = _Par::stateCore(); + if (not core.isValidIDX (newIDX)) + throw lumiera::error::Invalid ("Attempt to set index out of bounds", + lumiera::error::LUMIERA_ERROR_INDEX_BOUNDS); + core.idx_ = newIDX; + } + }; + + +} // namespace lib +#endif /*SRC_LIB_ITER_INDEX_H*/ diff --git a/tests/library/iter-index-test.cpp b/tests/library/iter-index-test.cpp new file mode 100644 index 000000000..611fb617a --- /dev/null +++ b/tests/library/iter-index-test.cpp @@ -0,0 +1,265 @@ +/* + IterIndex(Test) - verify index access packaged as iterator handle + + Copyright (C) Lumiera.org + 2024, Hermann Vosseler + + This program 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + +/** @file iter-index-test.cpp + ** unit test \ref IterIndex_test + */ + + + +#include "lib/test/run.hpp" +#include "lib/iter-index.hpp" +#include "lib/test/test-helper.hpp" +#include "lib/iter-explorer.hpp" +#include "lib/format-util.hpp" +#include "lib/util.hpp" + +#include + + + +namespace lib { +namespace test{ + + using ::Test; + using util::join; + using util::isnil; + using std::vector; + + using LERR_(ITER_EXHAUST); + using LERR_(INDEX_BOUNDS); + + + namespace { // test fixture + + const uint NUM_ELMS = 10; + + using Numz = vector; + using Iter = IterIndex; + using CIter = IterIndex; + + inline Numz + makeNumz() + { + Numz numz; + for (uint i=0; i + void + verifyComparisons (IT const& ii) + { + IT i1(ii); + IT i2(ii); + IT iN; + CHECK ( isnil (iN)); + CHECK (!isnil (i1)); + CHECK (!isnil (i2)); + + CHECK (i1 == i2); CHECK (i2 == i1); + CHECK (i1 != iN); CHECK (iN != i1); + CHECK (i2 != iN); CHECK (iN != i2); + + ++i1; + CHECK (i1 != i2); + CHECK (i1 != iN); + + ++i2; + CHECK (i1 == i2); + CHECK (i1 != iN); + CHECK (i2 != iN); + + while (++i1) { } + CHECK (isnil(i1)); + CHECK (i1 != i2); + CHECK (i1 == iN); + + while (++i2) { } + CHECK (isnil(i2)); + CHECK (i2 == i1); + CHECK (i2 == iN); + } + }; + + LAUNCHER (IterIndex_test, "unit common"); + + +}} // namespace lib::test + diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 8f6b652b8..fdad6391e 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -112553,16 +112553,13 @@ std::cout << tmpl.render({"what", "World"}) << s - - - +

das bedeutet: nachdem ein nested-context geöffnet wurde, müssen wir einen State erlangen, auf dem transparent genauso gearbeitet werden kann, wie auf dem initialen / top-level-State

- -
+
@@ -112577,16 +112574,13 @@ std::cout << tmpl.render({"what", "World"}) << s - - - +

weil sie im Einzelfall auch komplexer sein kann, und im Besonderen eine parent-Verknüpfung beinhaltet

- -
+
@@ -112846,6 +112840,7 @@ std::cout << tmpl.render({"what", "World"}) << s + @@ -112867,16 +112862,13 @@ std::cout << tmpl.render({"what", "World"}) << s - - - +

will sagen, der Standardfall ist, lediglich auf eine Map per Key zuzugreifen — und die gesamte Datenstruktur ist hierauf zu optimieren

- -
+
@@ -112908,6 +112900,13 @@ std::cout << tmpl.render({"what", "World"}) << s + + + + + + + @@ -112940,6 +112939,64 @@ std::cout << tmpl.render({"what", "World"}) << s
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ uuund fertig — C++ ist toll +

+ +
+ +
+
+ + + + + + + + + + + + +
@@ -120672,6 +120729,31 @@ class Something
+ + + + + + + + + + + + + + +

+ Konversion string_view ⟼ string kopiert die Daten +

+ +
+ + + + + +