From bd70c5faec2f8956ebd00c30add462796bfc6445 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 31 Jan 2025 19:29:04 +0100 Subject: [PATCH] Invocation: pick up work on the render node spec ...at the point where I identified the need to parse nested terms. The goals are still the same * write tests to ''verify connectivity'' of nodes generated by the new `NodeBuilder` * allow for ''extended custom attributes'' in the ProcID * provide the ability to mark specific parametrisations * build a Hash-Key to identify a given processing step __Note Library__: this is the first time `lib::Several` was used to hold a ''const object''. Some small adjustments in type detection were necessary to make that work. Access to stored data happens through the `lib::Several` front-end and thus always includes the const modifier; so casting any const-ness out of the way in the low-level memory management is not a concern... --- src/lib/several-builder.hpp | 57 +++- src/steam/engine/proc-id.hpp | 40 ++- src/steam/engine/proc-node.cpp | 14 + src/steam/engine/proc-node.hpp | 1 - tests/core/steam/engine/node-meta-test.cpp | 9 +- wiki/thinkPad.ichthyo.mm | 324 +++++++++++++++++++-- 6 files changed, 394 insertions(+), 51 deletions(-) diff --git a/src/lib/several-builder.hpp b/src/lib/several-builder.hpp index 115173686..947c772e4 100644 --- a/src/lib/several-builder.hpp +++ b/src/lib/several-builder.hpp @@ -97,9 +97,9 @@ #include "include/limits.hpp" #include "lib/iter-explorer.hpp" #include "lib/format-string.hpp" +#include "lib/meta/trait.hpp" #include "lib/util.hpp" -#include #include #include #include @@ -125,6 +125,7 @@ namespace lib { using util::max; using util::min; using util::_Fmt; + using util::unConst; using util::positiveDiff; using std::is_nothrow_move_constructible_v; using std::is_trivially_move_constructible_v; @@ -183,13 +184,16 @@ namespace lib { using AlloT = std::allocator_traits; using Bucket = ArrayBucket; + template + using XAlloT = typename AlloT::template rebind_traits>; + Allo& baseAllocator() { return *this; } template auto adaptAllocator() { - using XAllo = typename AlloT::template rebind_alloc; + using XAllo = typename XAlloT::allocator_type; if constexpr (std::is_constructible_v) return XAllo{baseAllocator()}; else @@ -229,7 +233,7 @@ namespace lib { ASSERT (storageBytes - offset >= cnt*spread); Bucket* bucket = reinterpret_cast (loc); - using BucketAlloT = typename AlloT::template rebind_traits; + using BucketAlloT = XAlloT; auto bucketAllo = adaptAllocator(); // Step-2 : construct the Bucket metadata | ▽ ArrayBucket ctor arg ▽ try { BucketAlloT::construct (bucketAllo, bucket, storageBytes, offset, spread); } @@ -247,9 +251,9 @@ namespace lib { createAt (Bucket* bucket, size_t idx, ARGS&& ...args) { REQUIRE (bucket); - using ElmAlloT = typename AlloT::template rebind_traits; + using ElmAlloT = XAlloT; auto elmAllo = adaptAllocator(); - E* loc = reinterpret_cast (& bucket->subscript (idx)); + E* loc = reinterpret_cast (& unConst(bucket->subscript (idx))); ElmAlloT::construct (elmAllo, loc, forward (args)...); ENSURE (loc); return *loc; @@ -270,11 +274,11 @@ namespace lib { if (not is_trivially_destructible_v) { size_t cnt = bucket->cnt; - using ElmAlloT = typename AlloT::template rebind_traits; + using ElmAlloT = XAlloT; auto elmAllo = adaptAllocator(); for (size_t idx=0; idx (& bucket->subscript (idx)); + E* elm = reinterpret_cast (& unConst(bucket->subscript (idx))); ElmAlloT::destroy (elmAllo, elm); } } @@ -479,6 +483,15 @@ namespace lib { return move(*this); } + /** consume all values exposed through an iterator by moving into the builder */ + template + SeveralBuilder&& + moveAll (SEQ& dataSrc) + { + explore(dataSrc).foreach ([this](auto it){ emplaceMove(it); }); + return move(*this); + } + /** emplace a number of elements of the defined element type \a E */ template SeveralBuilder&& @@ -540,6 +553,14 @@ namespace lib { emplaceNewElm (*dataSrc); } + template + void + emplaceMove (IT& dataSrc) + { + using Val = typename IT::value_type; + emplaceNewElm (move (*dataSrc)); + } + template void emplaceNewElm (ARGS&& ...args) @@ -716,22 +737,26 @@ namespace lib { Deleter selectDestructor() { + using IVal = typename lib::meta::Strip::TypeReferred; + using EVal = typename lib::meta::Strip::TypeReferred; + using TVal = typename lib::meta::Strip::TypeReferred; + typename Policy::Fac& factory(*this); - if (is_Subclass() and has_virtual_destructor_v) + if (is_Subclass() and has_virtual_destructor_v) { - __ensureMark (VIRTUAL); - return [factory](ArrayBucket* bucket){ unConst(factory).template destroy (bucket); }; + __ensureMark (VIRTUAL); + return [factory](ArrayBucket* bucket){ unConst(factory).template destroy (bucket); }; } - if (is_trivially_destructible_v) + if (is_trivially_destructible_v) { - __ensureMark (TRIVIAL); - return [factory](ArrayBucket* bucket){ unConst(factory).template destroy (bucket); }; + __ensureMark (TRIVIAL); + return [factory](ArrayBucket* bucket){ unConst(factory).template destroy (bucket); }; } - if (is_same_v and is_Subclass()) + if (is_same_v and is_Subclass()) { - __ensureMark (ELEMENT); - return [factory](ArrayBucket* bucket){ unConst(factory).template destroy (bucket); }; + __ensureMark (ELEMENT); + return [factory](ArrayBucket* bucket){ unConst(factory).template destroy (bucket); }; } throw err::Invalid{_Fmt{"Unsupported kind of destructor for element type %s."} % util::typeStr()}; diff --git a/src/steam/engine/proc-id.hpp b/src/steam/engine/proc-id.hpp index 2d6d4b9bf..5b5619c1d 100644 --- a/src/steam/engine/proc-id.hpp +++ b/src/steam/engine/proc-id.hpp @@ -20,7 +20,11 @@ ** of the media-processing-library binding plug-in to ensure that classification ** matches relevant semantic distinctions. In this context, "different" means ** that two functions produce _perceptibly different results_ — which also - ** implies that for equivalent IDs we can use cached calculation results. + ** implies that for equivalent IDs we can use cached calculation results. + ** \par Code arrangement + ** - proc-node.hpp is a shallow interface header (included rather widely) + ** - proc-id.hpp (this header) details node specifications and is used for the NodeBuidler + ** - proc-node.cpp acts as »module« and anchor for all implementation services ** ** ## Structure and syntax ** A complete processing-specification combines a high-level identification of the enclosing @@ -62,8 +66,8 @@ #include "lib/error.hpp" #include "lib/hash-standard.hpp" #include "lib/several.hpp" -//#include "steam/streamtype.hpp" +#include #include @@ -71,6 +75,7 @@ namespace steam { namespace engine { namespace err = lumiera::error; + using std::move; using lib::HashVal; using std::string; using StrView = std::string_view; @@ -134,6 +139,9 @@ namespace engine { string genNodeSpec(Leads&); string genSrcSpec (Leads&); ///< transitively enumerate all unique source nodes + struct ArgModel; + ArgModel genArgModel(); + friend bool operator== (ProcID const& l, ProcID const& r) { @@ -152,6 +160,34 @@ namespace engine { + /** + * Expanded information regarding node input and output. + * Requires [parsing the spec](\ref ProcID::genArgModel) for construction. + */ + struct ProcID::ArgModel + : util::MoveAssign + { + using Strings = lib::Several; + using iterator = Strings::iterator; + + Strings iArg; + Strings oArg; + + bool empty() const { return not hasArgs(); }; + bool hasArgs() const { return hasInArgs() or hasOutArgs();} + bool hasInArgs() const { return not iArg.empty(); } + bool hasOutArgs() const { return not oArg.empty(); } + uint inArity() const { return iArg.size(); } + uint outArity() const { return oArg.size(); } + + private: + ArgModel (Strings&& iarg, Strings&& oarg) + : iArg{move (iarg)} + , oArg{move (oarg)} + { } + friend ArgModel ProcID::genArgModel(); + }; + }} // namespace steam::engine #endif /*ENGINE_PROC_ID_H*/ diff --git a/src/steam/engine/proc-node.cpp b/src/steam/engine/proc-node.cpp index b7a3006fc..96551d5ed 100644 --- a/src/steam/engine/proc-node.cpp +++ b/src/steam/engine/proc-node.cpp @@ -21,6 +21,7 @@ #include "steam/engine/proc-id.hpp" #include "steam/engine/proc-node.hpp" +#include "lib/several-builder.hpp" #include "lib/iter-explorer.hpp" #include "lib/format-string.hpp" #include "lib/format-util.hpp" @@ -204,6 +205,19 @@ namespace engine { } + /** parse and dissect the argument specification */ + ProcID::ArgModel + ProcID::genArgModel() + { + using VecS = std::vector; + VecS v1{"bla","blubb"}; + VecS v2; + auto elms1 = lib::makeSeveral().appendAll(v1); + auto elms2 = lib::makeSeveral().appendAll(v2); + return ProcID::ArgModel{elms1.build(), elms2.build()}; + } + + /** * @return symbolic string with format `NodeSymb--` diff --git a/src/steam/engine/proc-node.hpp b/src/steam/engine/proc-node.hpp index 62ba708cc..6436f5b8d 100644 --- a/src/steam/engine/proc-node.hpp +++ b/src/steam/engine/proc-node.hpp @@ -108,7 +108,6 @@ #include "lib/several.hpp" #include -#include #include diff --git a/tests/core/steam/engine/node-meta-test.cpp b/tests/core/steam/engine/node-meta-test.cpp index 7e541859e..0889220fb 100644 --- a/tests/core/steam/engine/node-meta-test.cpp +++ b/tests/core/steam/engine/node-meta-test.cpp @@ -19,6 +19,7 @@ #include "lib/test/run.hpp" #include "steam/engine/proc-node.hpp" #include "steam/engine/node-builder.hpp" +#include "lib/format-util.hpp" //#include "steam/engine/test-rand-ontology.hpp" ///////////TODO #include "lib/test/diagnostic-output.hpp"/////////////////TODO //#include "lib/util.hpp" @@ -26,13 +27,14 @@ #include //using std::string; -using std::abs; namespace steam { namespace engine{ namespace test { + using std::abs; + using util::join; @@ -76,6 +78,11 @@ namespace test { CHECK (p3.genProcName() == "N3"_expect ); CHECK (p2.genProcSpec() == "U:N2.+(a1,a2)"_expect ); CHECK (p3.genProcSpec() == "O:N3(in/3)(o1,o2/2)"_expect ); + + ProcID::ArgModel arg1 = p1.genArgModel(); + ProcID::ArgModel arg2 = p2.genArgModel(); + ProcID::ArgModel arg3 = p3.genArgModel(); +SHOW_EXPR(join (arg1.iArg)) UNIMPLEMENTED ("parse and evaluate"); } diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 78675c0f4..7d0f19011 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -55167,9 +55167,10 @@ - + - + + @@ -55278,9 +55279,9 @@ - + - + @@ -55479,7 +55480,7 @@ - + @@ -55495,7 +55496,7 @@ - + @@ -55533,7 +55534,7 @@ - + @@ -55586,7 +55587,7 @@ - + @@ -56102,7 +56103,7 @@ - + @@ -56114,7 +56115,7 @@ - + @@ -56843,7 +56844,7 @@ - + @@ -57012,7 +57013,7 @@ - + @@ -57053,7 +57054,7 @@ - + @@ -57099,7 +57100,7 @@ - + @@ -57365,7 +57366,7 @@ - + @@ -58040,8 +58041,8 @@ - - + + @@ -102547,7 +102548,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -102822,7 +102823,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)

- Und das muß auch so sein, denn sonnst wäre das kein freier Extension-Point. Jede Abweichung davon bedeutet effektiv eine Einschränkung der Implementierung — oder anders ausgedrückt, eine Erweiterung des Port-Interfaces. Das wäre durchaus denkbar; hier geht es allerdings um den sekundären Belang, wie man eine solche Erweiterung mit möglichst geringen Kosten realisiert + Und das muß auch so sein, denn sonnst wäre das kein freier Extension-Point. Jede Abweichung davon bedeutet effektiv eine Einschränkung der Implementierung — oder anders ausgedrückt, eine Erweiterung des Port-Interfaces. Das wäre durchaus denkbar; hier geht es allerdings um den sekundären Belang, wie man eine solche Erweiterung mit möglichst geringen Kosten realisiert

@@ -102893,7 +102894,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) Die Kosten werden hierdurch in die Laufzeit verschoben, genauer gesagt, aus dem Code-Segment in die Allokation für Nodes. Es ist weiterhin Compile-Time-Logik erforderlich, um für den Standard-Fall ein entsprechendes Tag in den extended attributes zu hinterlassen.

- Zunächst einmal erzeugt diese Lösung einen erheblichen run-time-Storage-Overhead. Die dynamische Codierung ersetzt einen einzelnen Pointer in der VTable jeweils durch eine String-Spec mit vmtl. mehr als 8 char Länge, sowie dazu noch einen Eintrag in einer sekundären Dispatcher-Table, bestehend aus einer Hash-ID (size_t) plus ... genau einem Function-Pointer. So gesehen, erst mal ziemlich dämlich. ABER es gibt nun einen zusätzlichen Hebel durch de-Duplikation: zum Einen deduplizieren wir die Spec-Strings selber; was aber noch wichtiger ist, da die Detail-Operationen nur auf Teile der Spec-Strings differenzieren, wird es deutlich weniger Einträge in der sekundären Dispatcher-Table geben. Eine String-View kostet zwei «slot» — also wäre bei mehr als zwei  Zusatz-Operationen der Overhead in der ProcID selber bereits reingeholt. Allerdings gibt es noch viel zusätzliche String-Storage, und die Dispatcher-Table-Einträge. Ob sich diese amortisieren, hängt von der Differenzierung in der Praxis ab. Entsprechend gibt es auch noch den Trade-off, ob man in der ProcID weniger oder mehr Teil-Komponenten des Spec-String separat speichert + Zunächst einmal erzeugt diese Lösung einen erheblichen run-time-Storage-Overhead. Die dynamische Codierung ersetzt einen einzelnen Pointer in der VTable jeweils durch eine String-Spec mit vmtl. mehr als 8 char Länge, sowie dazu noch einen Eintrag in einer sekundären Dispatcher-Table, bestehend aus einer Hash-ID (size_t) plus ... genau einem Function-Pointer. So gesehen, erst mal ziemlich dämlich. ABER es gibt nun einen zusätzlichen Hebel durch de-Duplikation: zum Einen deduplizieren wir die Spec-Strings selber; was aber noch wichtiger ist, da die Detail-Operationen nur auf Teile der Spec-Strings differenzieren, wird es deutlich weniger Einträge in der sekundären Dispatcher-Table geben. Eine String-View kostet zwei «slot» — also wäre bei mehr als zwei Zusatz-Operationen der Overhead in der ProcID selber bereits reingeholt. Allerdings gibt es noch viel zusätzliche String-Storage, und die Dispatcher-Table-Einträge. Ob sich diese amortisieren, hängt von der Differenzierung in der Praxis ab. Entsprechend gibt es auch noch den Trade-off, ob man in der ProcID weniger oder mehr Teil-Komponenten des Spec-String separat speichert

@@ -105112,7 +105113,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
- + + @@ -105306,6 +105308,43 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + + + + + +

+ ich war blockiert mit dem testgetriebenen Ansatz, denn ich kann zwar jetzt Nodes bauen, aber nicht einfach verifizieren und dokumentieren, daß sie korrekt verdrahtet sind. +

+ + +
+
+ + + + +

+ die gleiche Funktionalität wird später zur Problem-Diagnose benötigt; also nichts performance-kritisches +

+ + +
+
+ + + + +

+ im Moment sieht's so aus, daß der erzeugende Code im Library-Plug-In ohnehin von internen strukturierten Daten ausgeht, um daraus die Node-Spec zu generieren. Diese wird dann einmal beim Builde per Parse zerlegt und ggfs noch zusätzlich dekoriert, und das war's dann. +

+ + +
+
+
@@ -105347,8 +105386,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + @@ -105377,7 +105416,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)

- + @@ -105391,10 +105430,141 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
+ + + + + + + + + + + + + + + +

+ ⟹ alle Deklarationen zum Spec / Model dorthin +

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

+ d.h. ArgumentModel könnte eine Struct sein, und lediglich zusätzliche Informations-Funktionen bieten +

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

+ ...mußte dazu erst mal eine Runde raus, dann hab ichs rasch gesehen... +

+ + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + +
+ + +
+
- + @@ -105403,7 +105573,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)

- + @@ -105414,9 +105584,84 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + + +

+ Das passiert, wenn man mit einer Implementierungtechnik anfängt, und nicht bei der Anforderungs-Analyse.... +

+

+ Ja, es würde definitiv gehen, und der User würde fluchen, denn die allgegenwärtigen Quotes haben die Nützlichkeit einer »einfachen Listen-Spec« wieder auf. Hinzu kommt, daß es einige spziell lästige Grenzfälle gibt, wie nested-Quotes , die man dann finden und escapen muß +

+ + +
+
+ + + + +

+ weiß noch nicht recht, wohin das mit der Node-Spec noch führen wird; nach der Lösung mit einer textuellen Spech habe ich vor allem gegriffen, da ich eine Festlegung auf ein Meta-Modell vermeiden möchte — da noch auf lange Zeit das Projekt nicht im Stande sein wird, das Feld der Möglichkeiten hier zu überblicken (was für Medien werden überhaupt verarbeitet? Außer Video und Sound, meine ich....) +

+ + +
+
+
+ + + + + +

+ ....und jetzt hab ich einen echten LL-Parser, der diese Aufgabe direkt und ohne Implementierungstricks handhaben kann +

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

+ ...deshalb habe ich ja auch die Node-Connectivity beibehalten (obwohl zum Rendern nur die Port-Connectivity gebraucht wird). Zusätzlich könnten noch Attribute in der ProcID eine Rolle spielen. Also insgesamt ehr kein separates Parsing +

+ + +
+
+
+ + + + +

+ ⟹ also so implementieren wie's am einfachsten geht +

+ + +
+
+
- + + @@ -105441,6 +105686,19 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + + +

+ Aua, dieses Thema ist zu lange liegen geblieben; drei Monate später weiß ich nicht mehr, was ich überhaupt bezwecken wollte....  Kam das mit der Pipeline so zustande, weil ich mir vorgestllt hatte, einen Spec-String mit einer RegExp zu »scannen« — denn so einfach wird es nicht sein +

+ + +
+ + +
@@ -105484,7 +105742,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -105497,7 +105755,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)

- eine »offene« Lösung würde einer Erweiterung von Lumiera (plug-in) oder einer speziellen Library-Integration ermöglichen, Attribute durch das low-level-Model hindurch zu »tunneln«, ohne spezielle Unterstützung der Core-Implementierung. Ich halte das jedoch aktuell für einen nicht naheliegenden Ansatz, da es keinen »Rück-Kanal« vom low-level-Model in die Erweiterungen/Plug-ins gibt; generell hat sich das low-level-Model zu einer internen Struktur entwickelt und kann nicht verstanden werden ohne implizite Kenntnis mancher Entwicklungs-Aspekte + eine »offene« Lösung würde einer Erweiterung von Lumiera (plug-in) oder einer speziellen Library-Integration ermöglichen, Attribute durch das low-level-Model hindurch zu »tunneln«, ohne spezielle Unterstützung der Core-Implementierung. Ich halte das jedoch aktuell für einen nicht naheliegenden Ansatz, da es keinen »Rück-Kanal« vom low-level-Model in die Erweiterungen/Plug-ins gibt; generell hat sich das low-level-Model von einer Schnittstelle weg und zu einer internen Struktur hin entwickelt und kann nicht verstanden werden ohne implizite Kenntnis mancher Entwicklungs-Aspekte

@@ -105632,7 +105890,11 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
- + + + + + @@ -152515,7 +152777,7 @@ std::cout << tmpl.render({"what", "World"}) << s - +