From df37fec500ccd727ae33124d23420df53f6c1724 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 22 Oct 2024 03:20:50 +0200 Subject: [PATCH] =?UTF-8?q?Invocation:=20switch=20`WeavingBuilder`=20to=20?= =?UTF-8?q?produce=20the=20result=20via=20=CE=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change allows to disentangle the usages of `lib::SeveralBuilder`, so that at any time during the build process only a single instance is actively populated, all in one row — and thus the required storage can either be pre-allocated, or dynamically extended and shrinked (when filling elements into the last `SeveralBuilder` currently activated) By packaging into a λ-closure, the building of the actual `Port` implementation objects (≙ `Turnout` instances) is delayed until the very end of the build process, and then unloaded into yet another `lib::Several` in one strike. Temporarily, those building functor objects are „hidden“ in the current stack frame, as a new `NodeBuilder` instance is dropped off with an adapted type parameter (embedding the λ-type produced by the last nested `PortBuilder` invocation, while inheriting from previous ones. However, defining a special constructor to cause this »chaining« poses some challenge (regarding overload resolution). Moreover, since the actual processing function shall be embedded directly (as opposed to wrapping it into a `std::function`), further problems can arise when this function is given as a ''function reference'' --- src/steam/engine/node-builder.hpp | 77 ++++++-- src/steam/engine/turnout.hpp | 22 ++- src/steam/engine/weaving-pattern-builder.hpp | 44 +++-- tests/core/steam/engine/node-linkage-test.cpp | 2 +- wiki/thinkPad.ichthyo.mm | 178 +++++++++++++----- 5 files changed, 244 insertions(+), 79 deletions(-) diff --git a/src/steam/engine/node-builder.hpp b/src/steam/engine/node-builder.hpp index b6ac96fac..62eb282b0 100644 --- a/src/steam/engine/node-builder.hpp +++ b/src/steam/engine/node-builder.hpp @@ -96,6 +96,7 @@ #include "steam/engine/turnout.hpp" #include "lib/several-builder.hpp" #include "lib/nocopy.hpp" +#include "lib/test/test-helper.hpp"/////////////////////TODO TOD-oh #include #include @@ -119,6 +120,7 @@ namespace engine { }//(End) policy + /** * A builder to collect working data. * Implemented through a suitable configuration of lib::SeveralBuilder, @@ -127,9 +129,35 @@ namespace engine { template using DataBuilder = lib::SeveralBuilder; + + template + class NodeBuilder; template class PortBuilderRoot; + + + namespace { // Metaprogramming helper to pick the proper constructor... + + using lib::meta::disable_if; + using std::bool_constant; + using std::__and_; + + template + struct ArgMatch : std::false_type { }; + +// template +// struct ArgMatch&&, SizMark, XS...> : std::true_type { }; // the chaining-ctor takes a NodeBuilder and a size-mark... + template + struct ArgMatch const&, XS...> : std::true_type { }; // the chaining-ctor takes a NodeBuilder and a size-mark... + + /** Metaprogramming: prevent the var-args ctor from shadowing the chaining ctor */ + template + using disable_if_Chain = disable_if<__and_ // do not use this constructor if 3 arguments given + ,ArgMatch // and these match the signature for the chaining-constructor + >>; + } + template class NodeBuilder @@ -139,30 +167,36 @@ namespace engine { using LeadRefs = DataBuilder; protected: - PortData ports_; ///////////////////////////////////////OOO obsolete and replaced by patternData_ LeadRefs leads_; - DAT patternData_; public: - template + template//, typename = disable_if_Chain> NodeBuilder (INIT&& ...alloInit) - : ports_{forward (alloInit)...} /////////////OOO obsolete and replaced by patternData_ - , leads_{forward (alloInit)...} - { } + : leads_{forward (alloInit)...} + { +//lib::test::TypeDebugger> muggi; +//lib::test::TypeDebugger> muggi; +// if constexpr (ArgMatch()) +// static_assert(not sizeof(POL), "YESSS!"); +// else +// static_assert(not sizeof(POL), "OH_NO"); + } template - NodeBuilder (NodeBuilder&& pred, BUILD&& entryBuilder) - : ports_{} /////////////OOO obsolete and replaced by patternData_ - , leads_{move (pred.leads_)} + NodeBuilder (NodeBuilder&& pred, SizMark, BUILD&& entryBuilder) + : leads_{move (pred.leads_)} , patternData_{move (pred.patternData_), forward (entryBuilder)} { } + template + friend class NodeBuilder; + NodeBuilder addLead (ProcNode const& lead) { - UNIMPLEMENTED ("append the given predecessor node to the sequence of leads"); + leads_.append (lead); return move(*this); } @@ -201,12 +235,18 @@ namespace engine { Connectivity build() { - return Connectivity{ports_.build() + PortData ports; + patternData_.collectEntries(ports); + return Connectivity{ports.build() ,leads_.build() ,NodeID{}}; //////////////////////////////////////OOO what's the purpose of the NodeID?? } }; + /** Deduction Guide: help the compiler with deducing follow-up NodeBuilder parameters */ + template + NodeBuilder (NodeBuilder&&, SizMark, BUILD&&) -> NodeBuilder>; + template @@ -319,14 +359,21 @@ namespace engine { /*************************************************************//** * Terminal: complete the Port wiring and return to the node level. */ - NodeBuilder ////////////////////////////////////////OOO need to extend and evolve the DAT parameter here + auto completePort() { //////////////////////////////////////////////////////////OOO need to provide all links to lead nodes here weavingBuilder_.fillRemainingBufferTypes(); - _Par::ports_.append(weavingBuilder_.build()); - return static_cast&&> (*this); - } // slice away the subclass + using MoThi = decltype(move(*this)); +// if constexpr (ArgMatch, void*>()) +// static_assert(not sizeof(POL), "YESSS!"); +// else +// static_assert(not sizeof(POL), "OH_NO"); +// lib::test::TypeDebugger> buggi; + return NodeBuilder{static_cast&&> (*this) //move (*this) + ,weavingBuilder_.sizMark + ,weavingBuilder_.build()}; + } // chain to builder with extended patternData private: template diff --git a/src/steam/engine/turnout.hpp b/src/steam/engine/turnout.hpp index 03d3eb2c7..cd3e7bd67 100644 --- a/src/steam/engine/turnout.hpp +++ b/src/steam/engine/turnout.hpp @@ -218,7 +218,6 @@ namespace engine { virtual BuffHandle allocateBuffer (const lumiera::StreamType* ty) { return current_.allocateBuffer(ty); } }; -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #1367 : Rebuild the Node Invocation /** @@ -255,6 +254,7 @@ namespace engine { return Strategy::step (*this); } }; +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #1367 : Rebuild the Node Invocation /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1367 : Rebuild the Node Invocation /** @@ -288,11 +288,11 @@ namespace engine { * + an array of output buffer pointers * + `CONF::MAX_SIZ` limits both arrays */ - template + template struct SimpleWeavingPattern - : CONF + : INVO { - using Feed = typename CONF::Feed; + using Feed = typename INVO::Feed; static_assert (_verify_usable_as_InvocationAdapter()); @@ -301,19 +301,23 @@ namespace engine { uint resultSlot{0}; - //////////////////////////////////////////OOO builder must set-up those descriptors + /** forwarding-ctor to provide the detailed input/output connections */ template - SimpleWeavingPattern(Several&& pr, Several&& dr, ARGS&& ...args) - : CONF{forward(args)...} + SimpleWeavingPattern (Several&& pr + ,Several&& dr + ,uint resultIdx + ,ARGS&& ...args) + : INVO{forward(args)...} , leadPort{move(pr)} , outTypes{move(dr)} + , resultSlot{resultIdx} { } Feed mount() { - return CONF::buildFeed(); + return INVO::buildFeed(); } void @@ -358,7 +362,7 @@ namespace engine { if (i != resultSlot) feed.outBuff[i].release(); } - ENSURE (resultSlot < CONF::MAX_SIZ, "invalid result buffer configured."); + ENSURE (resultSlot < INVO::MAX_SIZ, "invalid result buffer configured."); return feed.outBuff[resultSlot]; } diff --git a/src/steam/engine/weaving-pattern-builder.hpp b/src/steam/engine/weaving-pattern-builder.hpp index 85d325b91..4c6441379 100644 --- a/src/steam/engine/weaving-pattern-builder.hpp +++ b/src/steam/engine/weaving-pattern-builder.hpp @@ -198,7 +198,7 @@ namespace engine { * - use a sufficiently sized FeedManifold as storage scheme */ template - struct Conf_DirectFunctionInvocation + struct DirectFunctionInvocation : util::MoveOnly { using Manifold = FeedManifold; @@ -208,7 +208,7 @@ namespace engine { std::function buildFeed; // template - Conf_DirectFunctionInvocation(FUN fun) + DirectFunctionInvocation(FUN fun) : buildFeed{[=]//procFun = forward (fun)] { // using URGS = decltype(procFun); @@ -224,8 +224,11 @@ namespace engine { template using DataBuilder = lib::SeveralBuilder; + template + using SizMark = std::integral_constant; + -/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1367 : Prototyping: how to assemble a Turnout +/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1371 : Prototyping: how to assemble a Turnout /** * Recursive functional data structure to collect weaving pattern data @@ -273,18 +276,19 @@ namespace engine { template - using SimpleDirectInvoke = SimpleWeavingPattern>; + using SimpleDirectInvoke = SimpleWeavingPattern>; template struct WeavingBuilder : util::MoveOnly { - DataBuilder leadPort; - DataBuilder outTypes; + using TurnoutWeaving = Turnout>; + static constexpr SizMark sizMark{}; using TypeMarker = std::function; using ProviderRef = std::reference_wrapper; + DataBuilder leadPort; std::vector buffTypes; std::vector providers; @@ -339,19 +343,37 @@ namespace engine { auto build() { + // discard excess storage prior to allocating the output types sequence + leadPort.shrinkFit(); + maybeFillDefaultProviders (buffTypes.size()); REQUIRE (providers.size() == buffTypes.size()); + auto outTypes = DataBuilder{} + .reserve (buffTypes.size()); uint i=0; for (auto& typeConstructor : buffTypes) outTypes.append ( typeConstructor (providers[i++])); - ENSURE (leadPort.size() < N); - ENSURE (outTypes.size() < N); + ENSURE (leadPort.size() <= N); + ENSURE (outTypes.size() <= N); - using Product = Turnout>; - ///////////////////////////////OOO need a way to prepare SeveralBuilder-instances for leadPort and outDescr --> see NodeBuilder - return Product{leadPort.build(), outTypes.build(), move(fun_)}; + using PortDataBuilder = DataBuilder; + // provide a free-standing functor to build a suitable Port impl (≙Turnout) + return [leads = move(leadPort.build()) + ,types = move(outTypes.build()) + ,procFun = move(fun_) + ,resultIdx = resultSlot + ] + (PortDataBuilder& portData) mutable -> void + { +//lib::test::TypeDebugger uggi; + portData.template emplace (move(leads) + ,move(types) + ,resultIdx + ,move(procFun) + ); + }; } private: diff --git a/tests/core/steam/engine/node-linkage-test.cpp b/tests/core/steam/engine/node-linkage-test.cpp index ed9edc4c0..69cabe034 100644 --- a/tests/core/steam/engine/node-linkage-test.cpp +++ b/tests/core/steam/engine/node-linkage-test.cpp @@ -73,7 +73,7 @@ namespace test { { auto con = prepareNode() .preparePort() - .invoke(dummyOp) + .invoke(&dummyOp) .completePort() .build(); CHECK (isnil (con.leads)); diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index b93269ab0..924dd7298 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -8778,9 +8778,7 @@ - - - +

das ist aber unpraktisch.... @@ -8791,9 +8789,7 @@ - - - +

....also ist es gradezu natürlich, @@ -9414,9 +9410,7 @@ - - - +

der Natur der Dinge folgen, @@ -10352,9 +10346,7 @@ - - - +

versehentlich wurde auch der an std::forward gegeben @@ -12071,9 +12063,7 @@ - - - +

  • @@ -14417,9 +14407,7 @@ - - - +

    wer interpretiert @@ -18748,9 +18736,7 @@ - - - +

    das ist eine Design/Architektur-Entscheidung; ein generisches Widget soll noch nicht mit dem speziellen Belang einer Eichung in Zeiteinheiten belastet werden; dies hat dann jeweils ins abgeleiteten Klassen zu erfolgen, so z.B im Clip-Widget @@ -48420,9 +48406,7 @@ - - - +

    und delegiert iterativ @@ -49266,9 +49250,7 @@ - - - +

    Idee: context-bound @@ -49774,9 +49756,7 @@ - - - +

    Focus/Spot wird mitbewegt @@ -50410,9 +50390,7 @@ - - - +

    eine virtuelle Funktion @@ -50867,9 +50845,7 @@ - - - +

    aufruf direkt mit Command-ID -> erzeugt automatisch eine Klon-Kopie @@ -85659,6 +85635,11 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
    + + + + + @@ -87186,6 +87167,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
    + @@ -90005,7 +89987,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
    - + + + @@ -90065,6 +90049,10 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
    + + + +
    @@ -90250,8 +90238,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
    - - + + @@ -90263,15 +90251,118 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
    ...damit die terminale Methode des PortBuilders nun nicht mehr den originalen NodeBuilder per slice-copy extrahiert, sondern stattdessen eine angepaßte Variante erzeugen kann, bei der ein »Zwiebelschalen-Layer« um die PatternData gelegt wird, während alle weiteren Daten per move weitergeschoben werden

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

    + danach überhaupt erst outTypes = DataBuilder<POL, BuffDescr>  erzeugen +

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

    + der primäre Konstruktor nimmt Var-Args, und reicht sie zur Initialisierung des Allokators durch (was vor allem den wichtigen Fall eines monomorphic Allocators abdeckt, der gar keine Init-Argumente nimmt).... +

    + +
    +
    + + + + +

    + der andere ctor würde dann nur vorgezogen, wenn er exakt paßt +

    + +
    + + + +

    + Das sind die bekannten Regeln für die Overload-Selection: ein exakter Match ist stets besser als ein Match mit einer impliziten Konversion. Aber ein variadic Match ist schlechter als ein exakter Match. +

    + +
    +
    + + + + + + + +

    + hab jetzt zwei Stunden mit dem TypeDebugger die Instantiierungen auseinandergenommen, bis ich den Fehler gefunden hatte: die compile-time-Konstante ist eine static constexpr, wird also zu einer const&, und außerdem muß man dann auch noch das Strippen der RRef berücksichtigen, und eigentlich auch noch den Subtype match. Nee Danke! +

    + +
    - - + + + + +

    + ...muß hier zweistufig vorgehen mit einer Helper-Metafunction, die dann den genauen Match der Argumente prüft. +

    + +
    - - + + + + + + + +
    + + + + + + +

    + das ewige Ärgenis bei Function-Referenzen, die auf einen Function-Pointer decayen. Das passiert dann irgendwo mittendrin; konkret beim Binden in die Lambda-Closure, wo ich leider überhaupt keinen Einfluß nehmen kann. Grrrrrrr +

    + +
    +
    + + + + +

    + ...weil diese Funktion in der Praxis sehr häufig ein Lambda sein dürfte, das dann direkt kopiert und ge-Inlined werden kann. Bekanntermaßen ist nämlich std::function nicht sonderlich Inline-freundlich; in meinen Experimenten hat es manchmal geklappt, manchmal nicht, und dann bleibt eine Indirektion durch einen Funktion-Pointer übrig, was ich hier, auf einem Performance-kritischen Pfad gerne vermeiden möchte. Mal ganz abgesehen davon, daß eine std::function auch ziemlich »fett« ist (bei mir 5 »Slots« ≙ 40 Byte) — klar, aktuell haben wir das gleiche Problem auch in lib::Several, aber dort weiß ich bereits, daß es sich nahezu optimal lösen läßt, wenn man nur die entsprechende Komplexität in der Definition in Kauf nimmt (was ich auf später verschoben habe, siehe Ticket #) +

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