diff --git a/src/lib/format-util.hpp b/src/lib/format-util.hpp index b712181db..acacc2b1a 100644 --- a/src/lib/format-util.hpp +++ b/src/lib/format-util.hpp @@ -240,7 +240,7 @@ namespace util { return join (stringify (args...), "-"); } - /** shortcut: join directly with dashes */ + /** shortcut: join directly with dots */ template inline string joinDot (ARGS const& ...args) diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp index 70164bd23..1a0474cd3 100644 --- a/src/lib/iter-adapter.hpp +++ b/src/lib/iter-adapter.hpp @@ -578,6 +578,52 @@ namespace lib { + /** + * 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 ContainerCore + : public CON + { + using Iter = typename CON::iterator; + + Iter p_; + + public: + ContainerCore (CON&& container) + : CON(std::forward(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_; + } + }; + + + /** * Decorator-Adapter to make a »state core« iterable as Lumiera Forward Iterator. diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp index 11effb0df..ebb230c02 100644 --- a/src/lib/iter-explorer.hpp +++ b/src/lib/iter-explorer.hpp @@ -124,6 +124,7 @@ //Forward declaration to allow a default result container for IterExplorer::effuse namespace std { template class vector; + template class set; } @@ -1881,6 +1882,24 @@ namespace lib { } + /** preconfigured decorator to materialise, sort and deduplicate all source elements. + * @warning uses heap storage to effuse the source pipeline immediately + */ + template class SET =std::set> + auto + deduplicate() + { + using Value = typename meta::ValueTypeBinding::value_type; + using ResCore = ContainerCore>; + using ResIter = typename _DecoratorTraits::SrcIter; + SET buffer; + for (auto& val : *this) + buffer.emplace (val); + // »piggiy-back« the collected data into the result iterator + return IterExplorer{ResCore{move (buffer)}}; + } + + /** _terminal builder_ to package the processing pipeline as IterSource. * Invoking this function moves the whole iterator compound, as assembled by the preceding diff --git a/src/steam/engine/proc-id.hpp b/src/steam/engine/proc-id.hpp index 6c0712b22..f27b17dab 100644 --- a/src/steam/engine/proc-id.hpp +++ b/src/steam/engine/proc-id.hpp @@ -40,8 +40,9 @@ #define ENGINE_PROC_ID_H -#include "lib/hash-standard.hpp" #include "lib/error.hpp" +#include "lib/hash-standard.hpp" +#include "lib/several.hpp" //#include "steam/streamtype.hpp" #include @@ -55,6 +56,7 @@ namespace engine { using std::string; using StrView = std::string_view; + class ProcNode; class ProcID { @@ -64,14 +66,20 @@ namespace engine { ProcID (StrView nodeSymb, StrView portQual, StrView argLists); + using ProcNodeRef = std::reference_wrapper; + using Leads = lib::Several; + public: /** build and register a processing ID descriptor */ static ProcID& describe (StrView nodeSymb, StrView portSpec); /* === symbolic descriptors === */ - string genProcSpec(); ///< render a descriptor for the operation (without predecessors) - + string genProcName(); + string genProcSpec(); ///< render a descriptor for the operation (without predecessors) + string genNodeName(); + string genNodeSpec(Leads&); + string genSrcSpec (Leads&); ///< transitively enumerate all unique source nodes friend bool operator== (ProcID const& l, ProcID const& r) diff --git a/src/steam/engine/proc-node.cpp b/src/steam/engine/proc-node.cpp index ab8b19196..48a18b2bc 100644 --- a/src/steam/engine/proc-node.cpp +++ b/src/steam/engine/proc-node.cpp @@ -30,27 +30,32 @@ #include "steam/engine/proc-id.hpp" #include "steam/engine/proc-node.hpp" +#include "lib/iter-explorer.hpp" #include "lib/format-string.hpp" +#include "lib/format-util.hpp" #include "lib/util.hpp" #include #include +#include namespace steam { namespace engine { + using lib::explore; using util::_Fmt; using util::isnil; using util::unConst; using util::contains; using boost::hash_combine; - namespace { // Details... + namespace {// Details: registration and symbol table for node spec data... std::unordered_set procRegistry; std::unordered_set symbRegistry; + /** deduplicate and re-link to the entry in the symbol table */ void inline dedupSymbol (StrView& symbol) { @@ -60,7 +65,6 @@ namespace engine { } // (END) Details... -// using mobject::Placement; Port::~Port() { } ///< @remark VTables for the Port-Turnout hierarchy emitted from \ref proc-node.cpp @@ -96,14 +100,15 @@ namespace engine { /** @internal */ ProcID::ProcID (StrView nodeSymb, StrView portQual, StrView argLists) - : nodeSymb_{nodeSymb} /////////////////////////////////////////////////////////OOO intern these strings!! + : nodeSymb_{nodeSymb} , portQual_{portQual} , argLists_{argLists} { } - /** generate registry hash value based on the distinct data in ProcID. - * This function is intended to be picked up by ADL, and should be usable - * both with `std::hash` and ``. + /** + * generate registry hash value based on the distinct data in ProcID. + * This function is intended to be picked up by ADL, and should be usable + * both with `std::hash` and ``. */ HashVal hash_value (ProcID const& procID) @@ -115,6 +120,16 @@ namespace engine { return hash; } + string + ProcID::genProcName() + { + std::ostringstream buffer; + buffer << nodeSymb_; + if (not isnil(portQual_)) + buffer << '.' << portQual_; + return buffer.str(); + } + string ProcID::genProcSpec() { @@ -126,12 +141,65 @@ namespace engine { return buffer.str(); } + string + ProcID::genNodeName() + { + return string{nodeSymb_}; + } + namespace { // Helper to access ProcID recursively + ProcID& + procID (ProcNode& node) + { + REQUIRE (not isnil(watch(node).ports())); + return watch(node).ports().front().procID; + } + } + + string + ProcID::genNodeSpec (Leads& leads) + { + std::ostringstream buffer; + buffer << nodeSymb_; + if (1 != leads.size()) + buffer << genSrcSpec(leads); + else + { // single chain.... + ProcNode& p{leads.front().get()}; + buffer << "◁—" + << procID(p).genNodeName() // show immediate predecessor + << procID(p).genSrcSpec(leads); // and behind that recursively the source(s) + } + return buffer.str(); + } + + string + ProcID::genSrcSpec (Leads& leads) + { + return isnil(leads)? string{"-◎"} // no leads => starting point itself is a source node + : "┉┉{" + + util::join( + explore(leads) + .expandAll([](ProcNode& n){ return explore(watch(n).leads()); }) // depth-first expand all predecessors + .filter ([](ProcNode& n){ return watch(n).isSrc(); }) // but retain only leafs (≙ source nodes) + .transform([](ProcNode& n){ return procID(n).nodeSymb_;}) // render the node-symbol of each src + .deduplicate()) // sort and deduplicate + + "}"; + } + + + + /** + * @return symbolic string with format `NodeSymb--` + * @remark connectivity information is abbreviated and foremost + * indicates the data source(s) + */ string ProcNodeDiagnostic::getNodeSpec() { - UNIMPLEMENTED ("generate a descriptive Spec of this ProcNode for diagnostics"); + REQUIRE (not isnil(ports())); + return ports().front().procID.genNodeSpec (leads()); } HashVal diff --git a/src/steam/engine/proc-node.hpp b/src/steam/engine/proc-node.hpp index b8ddf486f..36db10025 100644 --- a/src/steam/engine/proc-node.hpp +++ b/src/steam/engine/proc-node.hpp @@ -239,6 +239,8 @@ namespace engine { auto& leads() { return n_.wiring_.leads; } auto& ports() { return n_.wiring_.ports; } + bool isSrc() { return n_.wiring_.leads.empty(); } + bool isValid() { diff --git a/tests/core/steam/engine/node-linkage-test.cpp b/tests/core/steam/engine/node-linkage-test.cpp index 7728ca9b8..029e7d941 100644 --- a/tests/core/steam/engine/node-linkage-test.cpp +++ b/tests/core/steam/engine/node-linkage-test.cpp @@ -85,10 +85,9 @@ namespace test { CHECK (watch(n1).isValid()); CHECK (watch(n1).leads().empty()); CHECK (watch(n1).ports().size() == 1); -SHOW_EXPR(watch(n1).getPortSpec(0)) + // can generate a symbolic spec to describe the Port's processing functionality... CHECK (watch(n1).getPortSpec(0) == "Test:Src.dummyFun(TestFrame)"_expect); -SHOW_EXPR(watch(n1).getPortSpec(1)) CHECK (watch(n1).getPortSpec(1) == "↯"_expect); // such a symbolic spec is actually generated by a deduplicated metadata descriptor @@ -103,15 +102,19 @@ SHOW_EXPR(watch(n1).getPortSpec(1)) CHECK (hash_value(meta1) != hash_value(meta2)); CHECK (hash_value(meta1) != hash_value(meta3)); -SHOW_EXPR(meta1.genProcSpec()); CHECK (meta1.genProcSpec() == "N1(arg)"_expect); -SHOW_EXPR(meta2.genProcSpec()); CHECK (meta2.genProcSpec() == "N2(arg)"_expect); -SHOW_EXPR(meta3.genProcSpec()); CHECK (meta3.genProcSpec() == "N1.uga()"_expect); + + // re-generate the descriptor for the source node (n1) auto& metaN1 = ProcID::describe("Test:Src",DUMMY_FUN_ID); -SHOW_EXPR(metaN1.genProcSpec()); CHECK (metaN1.genProcSpec() == "Test:Src.dummyFun(TestFrame)"_expect); +SHOW_EXPR(metaN1.genProcName()) + CHECK (metaN1.genProcName() == "Test:Src.dummyFun"_expect); +SHOW_EXPR(metaN1.genNodeName()) + CHECK (metaN1.genNodeName() == "Test:Src"_expect); +SHOW_EXPR(metaN1.genNodeSpec(con.leads)) + CHECK (metaN1.genNodeSpec(con.leads) == "Test:Src-◎"_expect); } diff --git a/tests/library/iter-explorer-test.cpp b/tests/library/iter-explorer-test.cpp index 3f283b8a9..39114607d 100644 --- a/tests/library/iter-explorer-test.cpp +++ b/tests/library/iter-explorer-test.cpp @@ -68,6 +68,7 @@ #include #include #include +#include namespace lib { @@ -291,6 +292,7 @@ namespace test{ verify_IterSource(); verify_reduceVal(); verify_effuse(); + verify_dedup(); verify_depthFirstExploration(); demonstrate_LayeredEvaluation(); @@ -1234,6 +1236,20 @@ namespace test{ } + /** @test verify to deduplicate the iterator's results into a std::set + */ + void + verify_dedup() + { + CHECK (materialise ( + explore(CountDown{23}) + .transform([](uint j){ return j % 5; }) + .deduplicate() + ) + == "0-1-2-3-4"_expect); // note: values were also sorted ascending by std::set + } + + /** @test package the resulting Iterator as automatically managed, diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index c3d648948..2c57f5f68 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -13732,9 +13732,7 @@ - - - +

technische Lösug diskutierbar @@ -14814,9 +14812,7 @@ - - - +

Spezialbehandlung @@ -16536,9 +16532,7 @@ - - - +

...damit es nicht versehentlich über einen anderen Layer gelegt wird, @@ -18963,9 +18957,7 @@ - - - +

BEDINGUNG: Constraint < Vollgröße @@ -46855,9 +46847,7 @@ - - - +

dann wird eine state mark ausgesendet @@ -47601,9 +47591,7 @@ - - - +

schlechter.... @@ -47994,9 +47982,7 @@ - - - +

NOTE: mutator need to be written in such a way @@ -48272,9 +48258,7 @@ - - - +

was man konventionellerweise auch macht. @@ -48423,9 +48407,7 @@ - - - +

man kann versuchen, die beiden Elemente der Duplikation aufzulösen. @@ -91373,8 +91355,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + @@ -91392,15 +91374,138 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + - + - + + + + + +

+ Wunsch: abgekürzte Darstellung der Vorläufer +

+ +
+ + + + + + + + + + + + + + +

+ ...darüber hab ich damals viel nachgedacht: eine generische Implementierung der Blatt-Iteration ist nicht so einfach zu realisieren, da man einem Element nicht anhand generischer Eigenschaften ansehen kann, ob es ein Blatt ist oder noch weiter expandiert werden kann. Ein Ausweg wäre, das Element versuchsweise zu expandieren und es selber nur zurückzuliefern, wenn die Expansion leer ist. Davon habe ich damals aber Abstand genommen, da es (a) erfordert, den Aufwand für die Expansion stets und für jedes Element zu leisten und (b) dann irgendwie eine Interaktion mit dem internen Stack stattfinden muß, und das dann auch noch rekursiv oder repretitiv  (und das wurde mir dann alles zu kompliziert) +

+ + +
+
+ + + + +

+ ...und deshalb können wir erst expandAll() machen, und dann die inneren Blattknoten einfach nachträglich wegfiltern, woduch automatisch weiter repetitiv konsumiert wird. +

+ + +
+
+
+
+ + + + + + + + + + + + +

+ ....aus Sicherheits-Gründen: der Container soll irgendwo „daneben“ in sicherer Storage liegen +

+ + +
+
+ + + + + + + + + + + + + + + + + +

+ Der Trick ist: die Funktion nimmt ein Template-Argument, das aber als Default den forward-deklarierten STL-Container hat. Scheint mit meinem GCC zu klappen .... ich bin da aber sehr skeptisch, denn die Signatur mit den Template-Parametern könnte sich in Zukunft schon noch erweitern..... +

+ + +
+ +
+ + + + +

+ auch da wollte ich einen pervasiven #include <map> vermeiden. Dann kam aber ein Defekt-Report von einem Clang-User auf BSD (und zwar der neueste Clang). Dort war der neue C++17 polymorphic-Allocator anders deklariert, so daß es zu einer ambiguity gekommen ist. +

+ +
+ +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + @@ -91665,6 +91770,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200

+
@@ -91749,7 +91855,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + @@ -91847,8 +91954,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - +