Library: extract the basic setup for a tuple-zipping iterator

Indeed the solution worked out yesterday could be extracted and turned generic.
Some in-depth testing is necessary though, and possibly some qualifications to allow pass-through of references...

Moreover, last days I started collecting notes regarding problem solving patterns,
which I tend to use frequently, but which might not be obvious and thus can easily
be forgotten. In fact, I had encountered several cases, where I did invent some
roughly similar solution repeatedly, having forgotten about already settled matters.

Hopefully the habit of collecting notes and hints at a central location serves to remedy
This commit is contained in:
Fischlurch 2024-11-22 19:11:53 +01:00
parent b6bdcc068d
commit f0eeabb29e
6 changed files with 155 additions and 38 deletions

View file

@ -38,7 +38,8 @@ visualisation::
into a relevant test, but commented-out there.
+
- generate Graphviz diagrams: 'lib/dot-gen.hpp' provides a simple DSL. See 'test-chain-load-test.cpp'
- generate Gnuplot scripts: use the Text-Template engine to fill in data, possibly from a data table
- generate Gnuplot scripts: use the xref:texttemplate[Text-Template] engine to fill in data, possibly
from a generated data table
* 'lib/gnuplot-gen.hpp' provides some pre-canned scripts for statistics plots
* used by the 'stress-test-rig.cpp', in combination with 'data.hpp' to collect measurement results
@ -147,8 +148,11 @@ Formatting
* the templated front-end passes-through most basic types and types with string-conversion
* all invocations are strictly error safe (never throw) and can thus be used from catch-handlers
- use the *Text-Template* engine. See 'text-template-test.cpp'. Can be used with simple map bindings,
but also from a `lib::GenNode` tree, or with a custom defined `DataSource` template
- use the anchor:texttemplate[lib/text-template]*Text-Template* engine. See 'text-template-test.cpp'.
Can be used with simple map bindings, or even a _definition string_ `"x=42, y=why-not?"`, but can also
draw data o from a `lib::GenNode` tree, or even from a custom defined `DataSource` template.
Supports placeholders, conditionals and simple loops (and that's it --
because there are way more capable solutions _out there_ ☺)
Language constructs
@ -166,7 +170,7 @@ build-from-anything::
- prevent shadowing of _automatically generated copy operations._
See https://issues.lumiera.org/ticket/963[#963]. Based on the ``disable if'' SFINAE technique.
A ready-made templated typedef `lib::metadisable_if_self` can be found in 'lib/meta/util.hpp'
A ready-made templated typedef `lib::meta::disable_if_self` can be found in 'lib/meta/util.hpp'
Variadics
@ -179,7 +183,7 @@ pick and manipulate individually::
- but sometimes it is easier to use the tried and true technique of the Loki-Typelists, which
can be programmed recursively, similar to LISP. The »bridge« is to unpack the variadic argument pack
into the `lib::meta::Types<ARGS...>` ([yellow-background]#⚠ still broken in 2024#
see https://issues.lumiera.org/ticket/987[#987], use `lib::meta::TySeq` from 'variadic-helper.hpp' as workaround...
see https://issues.lumiera.org/ticket/987[#987], use `lib::meta::TySeq` from 'variadic-helper.hpp' as workaround...)
+
apply functor to each tuple element::
A common trick is to use `std::apply` in combination with a _fold-expression_

View file

@ -2,6 +2,7 @@ Developer HOWTOs
================
//MENU: title Dev HOWTOs
//MENU: prepend child crackNuts
This section contains a loose collection of instructions, recipes, tutorials and
@ -9,8 +10,10 @@ similar usefull pieces of information targeted at Lumiera developers. See also
- the general link:{l}/project/faq.html[Lumiera FAQ]
- the link:{ldoc}/user/index.html[User documentation]
- link::{ldoc}/technical/code/index.html[Codebase organisation]
== Notepad
- a link:crackNuts.html[collection] of solution ideas
- link:DebugGdbPretty.html[Python pretty printers for GDB]
- link:HashFunctions.html[Notes regarding standard hash functions]

View file

@ -977,7 +977,8 @@ namespace lib {
/** convenience function to iterate "each number" */
template<typename INT>
inline NumIter<INT>
eachNum (INT start, INT end)
eachNum (INT start = std::numeric_limits<INT>::min()
,INT end = std::numeric_limits<INT>::max())
{
return NumIter<INT> (start, end);
}

View file

@ -24,13 +24,13 @@
#define LIB_ITER_ZIP_H
#include "lib/error.hpp"
#include "lib/iter-adapter.hpp"
#include "lib/iter-explorer.hpp"
#include "lib/meta/tuple-helper.hpp"
//#include "lib/util.hpp"
#include "lib/util.hpp"
//#include <deque>
//#include <utility>
#include <utility>
namespace lib {
@ -38,7 +38,59 @@ namespace lib {
// using util::unConst;
namespace iter {
/** */
/** construction-helper: apply IterExplorer builder packaged tuple */
template<typename...ITS>
auto
buildIterTuple (ITS&& ...iters)
{
return make_tuple (lib::explore (std::forward<ITS> (iters)) ...);
}
template<class ITUP>
class ProductCore
{
ITUP iters_;
public:
ProductCore(ITUP&& iterTup)
: iters_{move (iterTup)}
{ }
ProductCore() = default;
// default copy acceptable
friend bool
operator== (ProductCore const& cor1, ProductCore const& cor2)
{
return cor1.iters_ == cor2.iters_;
}
/* === »state core« protocol API === */
bool
checkPoint() const
{
bool active{true};
meta::forEach (iters_ // optimiser can unroll and short-circuit
,[&](auto& it){ active = active and bool(it); });
return active;
}
ITUP&
yield() const ///< exposing the iterator-tuple itself as »product«
{
return util::unConst(iters_);
}
void
iterNext()
{
meta::forEach (iters_
,[](auto& it){ ++it; });
}
};
} // namespace lib::iter
@ -46,7 +98,35 @@ namespace lib {
/** */
/** convenience free function to build an iterable sequence */
/**
* Build a tuple-combining iterator builder
* @param iters an arbitrary sequence of _iterable entities_
* @return an IterExplorer to yield result tuples on iteration
* @remark IterExplorer is both a »Lumiera Forward Itertor« and a _Pipeline Builder_
* - as Lumiera iterator, it can be used directly in _for-each_ and _while_ loops
* - result components can be picked up conveniently through _structural bindings_ for tuples
* - using the builder API, results can be postprocessed (apply a function), filtered, searched, reduced...
*/
template<class...ITS>
inline auto
zip (ITS&& ...iters)
{
auto access_result = [](auto& it){ return *it; };
auto tuple_results = [&](auto& it){ return meta::mapEach (*it, access_result); };
//
auto core = iter::ProductCore{iter::buildIterTuple (std::forward<ITS> (iters)...)};
//
return explore (std::move(core))
.transform (tuple_results);
}
/** tuple-combining iterator prefixed by index sequence */
template<class...ITS>
inline auto
izip (ITS&& ...iters)
{
return zip (eachNum<size_t>(), std::forward<ITS>(iters)...);
}

View file

@ -25,6 +25,7 @@
#include "lib/util.hpp"
#include <array>
#include <vector>
namespace lib {
@ -84,6 +85,7 @@ namespace test{
virtual void
run (Arg)
{
simpleUsage();
test_Fixture();
demo_mapToTuple();
demo_construction();
@ -91,6 +93,26 @@ namespace test{
}
/** @test demonstrate combined iteration */
void
simpleUsage()
{
auto a = std::array{1u,2u,3u};
auto v = std::vector{{2l,3l}};
for (auto [u,f] : zip(a,v))
CHECK (u + 1 == f);
auto it = izip(v);
CHECK (it);
SHOW_EXPR(*it)
++it;
SHOW_EXPR(*it)
CHECK (it);
++it;
CHECK (not it);
}
/** @test demonstrate how the test Fixture is used */
void
test_Fixture()
@ -120,7 +142,7 @@ namespace test{
CHECK (t1 == "«tuple<uint, double, char>»──(42,1.61803,7)"_expect ); // src-tuple t1 affected by side-effect
// tuple may hold a reference....
tuple<char, char&> t2{get<2>(t1),get<2>(t1ff)};
tuple<char, char&> t2{get<2>(t1), get<2>(t1ff)};
CHECK (t2 == "«tuple<char, char&>»──(7,7)"_expect );
auto t2f = mapEach (t2, [](auto& v){ v -= 1; return v; });
@ -139,11 +161,11 @@ namespace test{
CHECK (t2r == "«tuple<char&, char&>»──(6,6)"_expect ); // function yields references, which are placed into res-tuple
forEach (t2r, [](auto& v){ v +=23; });
CHECK (t2r == "«tuple<char&, char&>»──(M,M)"_expect ); // apply operation with side-effect to the last res-tuple t2r
CHECK (t2 == "«tuple<char, char&>»──(M,M)"_expect ); // the referred src-tuple t2 is also affected
CHECK (t2f == "«tuple<char, char>»──(6,6)"_expect ); // (while previously constructed t2f holds values unaffected)
CHECK (t1 == "«tuple<uint, double, char>»──(42,1.61803,7)"_expect ); // the first elm in t2 was bound by value, so no side-effect
CHECK (t1ff =="«tuple<uint, double, char>»──(42,1.61803,M)"_expect ); // but the second elm in t2 was bound by ref to t1ff
CHECK (t2r == "«tuple<char&, char&>»──(M,M)"_expect ); // apply operation with side-effect to the last res-tuple t2r
CHECK (t2 == "«tuple<char, char&>»──(M,M)"_expect ); // the referred src-tuple t2 is also affected
CHECK (t2f == "«tuple<char, char>»──(6,6)"_expect ); // (while previously constructed t2f holds values unaffected)
CHECK (t1 == "«tuple<uint, double, char>»──(42,1.61803,7)"_expect ); // the first elm in t2 was bound by value, so no side-effect
CHECK (t1ff == "«tuple<uint, double, char>»──(42,1.61803,M)"_expect ); // but the second elm in t2 was bound by ref to t1ff
}
@ -200,7 +222,7 @@ namespace test{
ITup&
yield() const
{
return unConst(iters_); // ◁─────────────── note: we expose the iterator-touple itself as »product«
return unConst(iters_); // ◁─────────────── note: we expose the iterator-tuple itself as »product«
}
void

View file

@ -52752,8 +52752,7 @@
das hei&#223;t, der erste ersch&#246;pfte Iterator terminiert die gesamte Sequenz
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1732154580080" ID="ID_336146444" MODIFIED="1732154642685" TEXT="wichtige Variante: zip-with-index">
<richcontent TYPE="NOTE"><html>
@ -52763,8 +52762,7 @@
damit man auch die F&#228;lle erschlagen kann, in denen irgendwo eine Index-Variable gebraucht wird; oft ist das n&#228;mlich der einzige Grund, dann doch eine klassische For-Iteration zu machen
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
</node>
<node CREATED="1732154647647" ID="ID_232099852" MODIFIED="1732154650378" TEXT="Technologie">
@ -52811,8 +52809,7 @@
stelle fest: ich <b>darf kein Quell-Tupel</b>&#160;konstruieren
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1732234492677" ID="ID_1711720667" MODIFIED="1732234609138" TEXT="weil dann das Thema perfect-forwarding dazwischen liegt">
<linktarget COLOR="#d41f46" DESTINATION="ID_1711720667" ENDARROW="Default" ENDINCLINATION="-179;-10;" ID="Arrow_ID_880963824" SOURCE="ID_1112527704" STARTARROW="None" STARTINCLINATION="176;10;"/>
@ -52831,8 +52828,7 @@
Es ist eigentlich nur eine Verpackung f&#252;r std::apply, welches man mit dem gleichen Aufruf-Aufwand stets auch direkt aufrufen k&#246;nnte. Hinzu kommt, da&#223; nun die Argument-Ordnung auf dem API entweder links-rum oder rechts-rum nicht pa&#223;t und verwirrend ist
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1732234715777" ID="ID_1569189170" MODIFIED="1732234822377" TEXT="braucht abar auch gar nicht den Indirektions-Trick">
<richcontent TYPE="NOTE"><html>
@ -52891,8 +52887,7 @@
...und damit sind n&#228;mlich beide Ans&#228;tze letzlich wieder vers&#246;hnt. Das war die wichtige Einsicht beim zweiten Design-Anlauf. Beim ersten Anlauf wollte ich &#187;brav&#171; sein und Werte liefern, und das ganze Design wurde dadurch extrem undurchschaubar und wackelig, denn nat&#252;rlich wurde beim ersten Kontakt mit der Realit&#228;t klar, da&#223; man dann smart-Pointer durch die Gegend schiebt.
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1732233861279" ID="ID_1970755076" MODIFIED="1732234153457" TEXT="das ist ein ganz zentrales Element in meinem Design">
<richcontent TYPE="NOTE"><html>
@ -52939,8 +52934,7 @@
(ob dieserer test portabel ist....)
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<font NAME="SansSerif" SIZE="10"/>
<icon BUILTIN="clanbomber"/>
</node>
@ -52952,18 +52946,34 @@
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1732235208972" ID="ID_1764687143" MODIFIED="1732235216084" TEXT="nun in eine Klasse extrahieren">
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1732235208972" ID="ID_1764687143" MODIFIED="1732308740170" TEXT="nun in Library-Code extrahieren">
<icon BUILTIN="pencil"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1732299521810" ID="ID_185815307" MODIFIED="1732299541201" TEXT="schlank halten: es gen&#xfc;gt die ProductCore">
<icon BUILTIN="yes"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1732299561444" ID="ID_1140916206" MODIFIED="1732299624657" TEXT="dazu eine free-function iter::buildIterTuple(its...)">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1732308743486" ID="ID_1261102696" MODIFIED="1732308787453" TEXT="Pr&#xfc;fen und erweitern auf das Durchreichen von Referenzen">
<linktarget COLOR="#b36e78" DESTINATION="ID_1261102696" ENDARROW="Default" ENDINCLINATION="243;11;" ID="Arrow_ID_1182778536" SOURCE="ID_1663970369" STARTARROW="None" STARTINCLINATION="212;14;"/>
<icon BUILTIN="flag-pink"/>
</node>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1732235272468" ID="ID_1892180908" MODIFIED="1732235318053" TEXT="gr&#xfc;ndlich testen">
<icon BUILTIN="flag-pink"/>
<node COLOR="#338800" CREATED="1732308670586" ID="ID_351914878" MODIFIED="1732308678431" TEXT="einfaches Demo-Beispiel">
<icon BUILTIN="button_ok"/>
<node COLOR="#435e98" CREATED="1732308679924" ID="ID_762659981" MODIFIED="1732308713186" TEXT="sollte eine For-Schleife zeigen"/>
<node COLOR="#435e98" CREATED="1732308687234" ID="ID_981998183" MODIFIED="1732308713186" TEXT="sollte direkten Gebrauch als Iterator sichtbar machen"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1732235323997" ID="ID_1085200650" MODIFIED="1732235555231" TEXT="Iteratoren verschiedenen Typs">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1732235343138" ID="ID_1943784376" MODIFIED="1732235555231" TEXT="per structured Binding verarbeiten">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1732235434406" ID="ID_1663970369" MODIFIED="1732235555231" TEXT="Durchgriff auf die Quellen per Referenz">
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1732235434406" ID="ID_1663970369" MODIFIED="1732308796716" TEXT="Durchgriff auf die Quellen per Referenz">
<arrowlink COLOR="#b36e78" DESTINATION="ID_1261102696" ENDARROW="Default" ENDINCLINATION="243;11;" ID="Arrow_ID_1182778536" STARTARROW="None" STARTINCLINATION="212;14;"/>
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1732235523602" ID="ID_1940842867" MODIFIED="1732235555232" TEXT="Durchgriff auf Child-Expander im Quell-Iterator">
@ -93132,8 +93142,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
OutputSlotProtocol_test
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="broken-line"/>
<node CREATED="1732224193493" ID="ID_279656548" MODIFIED="1732224303644" TEXT="hat wohl alles etws mit der Verifikation von TestFrame zu tun">
<richcontent TYPE="NOTE"><html>
@ -93146,8 +93155,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<font face="Monospaced" size="1">0000000516: CHECK: buffer-provider-protocol-test.cpp:107: thread_1: verifySimpleUsage: (</font><font face="Monospaced" size="1" color="#a70202">testData(0) == checker.accessMemory (0)</font><font face="Monospaced" size="1">) </font>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="idea"/>
</node>
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1732224312319" ID="ID_1805320851" MODIFIED="1732224425810" TEXT="Hypothesen...">
@ -93166,8 +93174,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</li>
</ul>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="hourglass"/>
</node>
</node>