diff --git a/doc/technical/howto/crackNuts.txt b/doc/technical/howto/crackNuts.txt index 5695ee798..f444a8d02 100644 --- a/doc/technical/howto/crackNuts.txt +++ b/doc/technical/howto/crackNuts.txt @@ -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` ([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_ diff --git a/doc/technical/howto/index.txt b/doc/technical/howto/index.txt index 49970fc99..6a32b998f 100644 --- a/doc/technical/howto/index.txt +++ b/doc/technical/howto/index.txt @@ -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] diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp index 313f50509..2da826564 100644 --- a/src/lib/iter-adapter.hpp +++ b/src/lib/iter-adapter.hpp @@ -977,7 +977,8 @@ namespace lib { /** convenience function to iterate "each number" */ template inline NumIter - eachNum (INT start, INT end) + eachNum (INT start = std::numeric_limits::min() + ,INT end = std::numeric_limits::max()) { return NumIter (start, end); } diff --git a/src/lib/iter-zip.hpp b/src/lib/iter-zip.hpp index a3bfd88f7..5e46b5398 100644 --- a/src/lib/iter-zip.hpp +++ b/src/lib/iter-zip.hpp @@ -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 -//#include +#include namespace lib { @@ -38,7 +38,59 @@ namespace lib { // using util::unConst; namespace iter { - /** */ + + /** construction-helper: apply IterExplorer builder packaged tuple */ + template + auto + buildIterTuple (ITS&& ...iters) + { + return make_tuple (lib::explore (std::forward (iters)) ...); + } + + template + 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 + 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 (iters)...)}; + // + return explore (std::move(core)) + .transform (tuple_results); + } + + /** tuple-combining iterator prefixed by index sequence */ + template + inline auto + izip (ITS&& ...iters) + { + return zip (eachNum(), std::forward(iters)...); + } diff --git a/tests/library/iter-zip-test.cpp b/tests/library/iter-zip-test.cpp index 5143df1b9..5def68269 100644 --- a/tests/library/iter-zip-test.cpp +++ b/tests/library/iter-zip-test.cpp @@ -25,6 +25,7 @@ #include "lib/util.hpp" #include +#include 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»──(42,1.61803,7)"_expect ); // src-tuple t1 affected by side-effect // tuple may hold a reference.... - tuple t2{get<2>(t1),get<2>(t1ff)}; + tuple t2{get<2>(t1), get<2>(t1ff)}; CHECK (t2 == "«tuple»──(7,7)"_expect ); auto t2f = mapEach (t2, [](auto& v){ v -= 1; return v; }); @@ -139,11 +161,11 @@ namespace test{ CHECK (t2r == "«tuple»──(6,6)"_expect ); // function yields references, which are placed into res-tuple forEach (t2r, [](auto& v){ v +=23; }); - CHECK (t2r == "«tuple»──(M,M)"_expect ); // apply operation with side-effect to the last res-tuple t2r - CHECK (t2 == "«tuple»──(M,M)"_expect ); // the referred src-tuple t2 is also affected - CHECK (t2f == "«tuple»──(6,6)"_expect ); // (while previously constructed t2f holds values unaffected) - CHECK (t1 == "«tuple»──(42,1.61803,7)"_expect ); // the first elm in t2 was bound by value, so no side-effect - CHECK (t1ff =="«tuple»──(42,1.61803,M)"_expect ); // but the second elm in t2 was bound by ref to t1ff + CHECK (t2r == "«tuple»──(M,M)"_expect ); // apply operation with side-effect to the last res-tuple t2r + CHECK (t2 == "«tuple»──(M,M)"_expect ); // the referred src-tuple t2 is also affected + CHECK (t2f == "«tuple»──(6,6)"_expect ); // (while previously constructed t2f holds values unaffected) + CHECK (t1 == "«tuple»──(42,1.61803,7)"_expect ); // the first elm in t2 was bound by value, so no side-effect + CHECK (t1ff == "«tuple»──(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 diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 8a096cc92..fbd688681 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -52752,8 +52752,7 @@ das heißt, der erste erschöpfte Iterator terminiert die gesamte Sequenz

- - + @@ -52763,8 +52762,7 @@ damit man auch die Fälle erschlagen kann, in denen irgendwo eine Index-Variable gebraucht wird; oft ist das nämlich der einzige Grund, dann doch eine klassische For-Iteration zu machen

- -
+
@@ -52811,8 +52809,7 @@ stelle fest: ich darf kein Quell-Tupel konstruieren

- - +
@@ -52831,8 +52828,7 @@ Es ist eigentlich nur eine Verpackung für std::apply, welches man mit dem gleichen Aufruf-Aufwand stets auch direkt aufrufen könnte. Hinzu kommt, daß nun die Argument-Ordnung auf dem API entweder links-rum oder rechts-rum nicht paßt und verwirrend ist

- - +
@@ -52891,8 +52887,7 @@ ...und damit sind nämlich beide Ansätze letzlich wieder versöhnt. Das war die wichtige Einsicht beim zweiten Design-Anlauf. Beim ersten Anlauf wollte ich »brav« sein und Werte liefern, und das ganze Design wurde dadurch extrem undurchschaubar und wackelig, denn natürlich wurde beim ersten Kontakt mit der Realität klar, daß man dann smart-Pointer durch die Gegend schiebt.

- -
+
@@ -52939,8 +52934,7 @@ (ob dieserer test portabel ist....)

- -
+
@@ -52952,18 +52946,34 @@ - + + + + + + + + + + + + + + + + - + + @@ -93132,8 +93142,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
OutputSlotProtocol_test

- - + @@ -93146,8 +93155,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
0000000516: CHECK: buffer-provider-protocol-test.cpp:107: thread_1: verifySimpleUsage: (testData(0) == checker.accessMemory (0))

- -
+
@@ -93166,8 +93174,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - +