From cf4bc380b01ae00c19a504a9b484b17e5f4aaecc Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 17 Dec 2024 23:32:11 +0100 Subject: [PATCH] Invocation: break-through with generic implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changeset is a sketch how to switch the entire implementation of the ''Invocation Adatper'' over to a generic argument usage scheme. This requires the ability to - detect if some argument is actually a ''structured type'' - investigate components of such a structured type to draw a distinction between »Buffer« and »Parameter« - ''lift'' the implementation of simple values to work on tuples - provide a way to ''bridge'' from ''tuple-style'' programming to ''array access'' As a building block, we use a new iteration-over-index construct, based on an idea discussed in https://stackoverflow.com/q/53522781/444796 The trick is to pass a `std::integer_constant` to a λ-generic --- src/lib/meta/variadic-helper.hpp | 80 +++- src/steam/engine/feed-manifold.hpp | 155 +++++++- tests/core/steam/engine/node-base-test.cpp | 24 +- wiki/thinkPad.ichthyo.mm | 436 +++++++++++++++++---- 4 files changed, 588 insertions(+), 107 deletions(-) diff --git a/src/lib/meta/variadic-helper.hpp b/src/lib/meta/variadic-helper.hpp index c3ab5087f..557b1f938 100644 --- a/src/lib/meta/variadic-helper.hpp +++ b/src/lib/meta/variadic-helper.hpp @@ -116,24 +116,6 @@ namespace meta { -//////////////////////////TICKET #943 : temporary workaround until full C++14 compliance (need constexpr) - template - constexpr inline X const& - max (X const& a, X const& b) - { - return a < b? b : a; - } - - template - constexpr inline X const& - min (X const& a, X const& b) - { - return b < a? b : a; - } -//////////////////////////TICKET #943 : temporary workaround until full C++14 compliance (need constexpr) - - - @@ -169,7 +151,7 @@ namespace meta { using FilledWith = typename BuildIndexSeq::template FilledWith::template AppendElm; template - using First = typename BuildIndexSeq::Ascending; + using First = typename BuildIndexSeq::Ascending; template using After = typename BuildIndexSeq< (n>c)? n-c : 0>::template OffsetBy; @@ -250,7 +232,6 @@ namespace meta { - /* ==== Rebinding Variadic Arguments ==== **/ /** @@ -277,6 +258,65 @@ namespace meta { + + /* ==== Invoke with index from variadic ==== **/ + + /** helper to invoke a functor, passing instances of std::integral_constant + * @tparam N size of the index-sequence to use for instantiation + */ + template + class WithIdxSeq + { + template + static void + invoke (FUN&& fun, std::index_sequence) + { + (fun (std::integral_constant{}), ...); + } + + public: + template + static void + invoke (FUN&& fun) + { + invoke (std::forward(fun), std::make_index_sequence{}); + } + }; + + /** + * Invoke a function (or λ) with index numbers derived from some variadic count. + * Notably this construct can be used for compile-time iteration over a structure. + * Instances of `std::integral_constant` are passed in sequence to the functor. + * The _size_ of the index sequence is derived from the following sources + * - if the type \a TTX is _tuple-like,_ then std::tuple_size is used + * - otherwise, if the type is a loki-style type sequence or type list, + * the number of type nodes is used + * - otherwise, as fall-back the number of template parameters is used + */ + template + inline void + forEachIDX (FUN&& fun) + { + auto N = []{ + if constexpr (is_Structured()) + return size_t(std::tuple_size::value); + else + if constexpr (lib::meta::is_Typelist::value) + return lib::meta::count::value; + else + { // Fallback: rebind template arguments into a type sequence + using Seq = typename RebindVariadic::Type; + return size_t(count::value); + } + }; + + WithIdxSeq::invoke (std::forward (fun)); + } + + + + + diff --git a/src/steam/engine/feed-manifold.hpp b/src/steam/engine/feed-manifold.hpp index 3291f3d16..441315e9d 100644 --- a/src/steam/engine/feed-manifold.hpp +++ b/src/steam/engine/feed-manifold.hpp @@ -85,10 +85,14 @@ #include "lib/meta/function.hpp" #include "lib/meta/trait.hpp" #include "lib/meta/typeseq-util.hpp" +#include "lib/meta/variadic-helper.hpp" +#include "lib/meta/generator.hpp" +#include "lib/test/test-helper.hpp" //#include "lib/several.hpp" //#include //#include +#include ////////////////////////////////TICKET #826 12/2024 the invocation sequence has been reworked and reoriented for integration with the Scheduler @@ -108,8 +112,12 @@ namespace engine { using std::remove_reference_t; using lib::meta::enable_if; using lib::meta::is_Structured; + using lib::meta::forEachIDX; + using lib::meta::TySeq; using std::is_pointer; using std::is_reference; + using std::remove_pointer_t; + using std::tuple_element_t; using std::tuple_size_v; using std::void_t; using std::__and_; @@ -128,16 +136,66 @@ namespace engine { template struct is_Buffer - : __and_<__not_> - ,__not_> - ,std::is_default_constructible + : __and_ ,__not_<_Fun> + ,std::is_default_constructible> > { }; + template class COND> + struct isAllElements + { + template + struct AndAll; + template + struct AndAll> + { + static constexpr bool value = + __and_> ... + >::value; + }; + using Elms = std::make_index_sequence>; + static constexpr bool value = AndAll::value; + }; + template + struct is_StructBuffs + : std::false_type + { }; + template + struct is_StructBuffs> > + : isAllElements + { }; + + + template + struct StructType + { +// static lib::test::TypeDebugger kacki; + + using Seq = TySeq; + using Tup = std::tuple; + }; + + template + struct StructType> > + { +// static lib::test::TypeDebugger drecky; + + template + struct AllZ; + template + struct AllZ> + { + using Seq = TySeq ...>; + }; + + using Elms = std::make_index_sequence>; + using Seq = typename AllZ::Seq; + using Tup = TUP; + }; /** Helper to pick up the parameter dimensions from the processing function * @remark this is the rather simple yet common case that media processing @@ -170,18 +228,27 @@ namespace engine { struct _ArgTrait>> { using Buff = PAR; ////////////////////////////////OOO not correct, remove this! + using Args = TySeq; enum{ SIZ = 1 }; }; template - struct _ArgTrait>> + struct _ArgTrait>> { using Buff = BUF; + using Args = TySeq; enum{ SIZ = 1 }; }; +// template +// struct _ArgTrait>> +// { +// using Args = typename StructType::Seq; +// enum{ SIZ = std::tuple_size_v }; +// }; template struct _ArgTrait> { using Buff = BUF; + using Args = TySeq;///////////////////////////OOO Schmuh!!! enum{ SIZ = N }; }; @@ -202,7 +269,7 @@ namespace engine { struct _Case>> { enum{ SLOT_O = 1 - , SLOT_I = is_Value<_Arg>()? 1 : 0 + , SLOT_I = is_StructBuffs<_Arg>::value? 0 : 1 ////////OOO maybe derive directly from std::conditional? }; }; template @@ -216,6 +283,9 @@ namespace engine { using SigI = _Arg::SLOT_I>; using SigO = _Arg::SLOT_O>; using SigP = _Arg; + using ArgI = typename _ArgTrait::Args; + using ArgO = typename _ArgTrait::Args; + using ArgP = typename _ArgTrait::Args; using BuffI = typename _ArgTrait::Buff; using BuffO = typename _ArgTrait::Buff; @@ -238,7 +308,7 @@ namespace engine { struct ParamStorage { using ParSig = typename _ProcFun::SigP; - ParSig param; + ParSig param{}; }; template @@ -248,7 +318,7 @@ namespace engine { using ArgSig = typename _ProcFun::SigI; BuffS inBuff; - ArgSig inArgs; + ArgSig inArgs{}; }; template @@ -258,7 +328,7 @@ namespace engine { using ArgSig = typename _ProcFun::SigO; BuffS outBuff; - ArgSig outArgs; + ArgSig outArgs{}; }; template @@ -307,9 +377,78 @@ namespace engine { : FeedManifold_StorageSetup { using _Trait = _ProcFun; + using _F = FeedManifold; static constexpr bool hasInput() { return _Trait::hasInput(); } static constexpr bool hasParam() { return _Trait::hasParam(); } + + using ArgI = typename _Trait::SigI; + using ArgO = typename _Trait::SigO; + enum{ FAN_I = _Trait::FAN_I + , FAN_O = _Trait::FAN_O + }; + + + FUN process; + + template + FeedManifold (INIT&& ...funSetup) + : process{forward (funSetup)...} + { } + + + using TupI = typename StructType::Tup; + using TupO = typename StructType::Tup; + + template + auto& + accessArg (ARG& arg) + { + if constexpr (is_Structured()) + return std::get (arg); + else + return arg; + } + + void + connect() + { + if constexpr (hasInput()) + { + forEachIDX ([&](auto i) + { + using BuffI = remove_pointer_t>; + accessArg (_F::inArgs) = & _F::inBuff[i].template accessAs(); + }); +// if constexpr (is_Structured()) +// for (uint i=0; i (_F::inArgs) = & _F::inBuff[i].template accessAs(); +// else +// _F::inArgs = & _F::inBuff[0].template accessAs(); + } + // always wire output buffer(s) + { + forEachIDX ([&](auto i) + { + using BuffO = remove_pointer_t>; + accessArg (_F::outArgs) = & _F::outBuff[i].template accessAs(); + }); +// if constexpr (is_Structured()) +// for (uint i=0; i (_F::outArgs) = & _F::outBuff[i].template accessAs(); +// else +// _F::outArgs = & _F::inBuff[0].template accessAs(); + } + } + + void + invoke() + { + if constexpr (hasInput()) + process (_F::inArgs, _F::outArgs); + else + process (_F::outArgs); + } }; diff --git a/tests/core/steam/engine/node-base-test.cpp b/tests/core/steam/engine/node-base-test.cpp index 3632955e5..af60887fe 100644 --- a/tests/core/steam/engine/node-base-test.cpp +++ b/tests/core/steam/engine/node-base-test.cpp @@ -26,10 +26,13 @@ #include "steam/engine/diagnostic-buffer-provider.hpp" #include "steam/engine/buffhandle-attach.hpp" //#include "lib/format-cout.hpp" +#include "lib/test/diagnostic-output.hpp"/////////////////////TODO //#include "lib/util.hpp" //using std::string; +using std::tuple;/////////////TODO + using std::array; namespace steam { @@ -77,13 +80,20 @@ namespace test { // Type setup to build a suitable FeedManifold using Buffer = long; - +/////////////////////////////////////////////////////////////////////////////////TODO + using T1 = tuple; + using T2 = array; + using T3 = int; + using T4 = int*; + using T5 = lib::HeteroData; +/////////////////////////////////////////////////////////////////////////////////TODO auto fun_singleOut = [&](Buffer* buff) { *buff = r1; }; using M1 = FeedManifold; CHECK (not M1::hasInput()); CHECK (not M1::hasParam()); - M1 m1{}; + M1 m1{fun_singleOut}; CHECK (1 == m1.outBuff.array().size()); +SHOW_EXPR(m1.outArgs) CHECK (nullptr == m1.outArgs ); // CHECK (m1.inArgs ); // does not compile because storage field is not provided // CHECK (m1.param ); @@ -96,6 +106,16 @@ namespace test { m1.outBuff.createAt (0, buff); CHECK (m1.outBuff[0].isValid()); CHECK (m1.outBuff[0].accessAs() == -55); + +SHOW_TYPE(M1::ArgI) +SHOW_TYPE(M1::TupI) +SHOW_TYPE(M1::ArgO) +SHOW_TYPE(M1::TupO) + m1.connect(); +SHOW_EXPR(m1.outArgs) +SHOW_EXPR(m1.outBuff[0]) +SHOW_EXPR(util::showAdr(*buff)) +SHOW_EXPR(util::showAdr(*m1.outBuff[0])) } }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 58be2cc87..cae1f64ae 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -24458,9 +24458,7 @@ - - - +

nicht mehr direkt auf den BodyCanvas als Größe anwenden @@ -25058,9 +25056,7 @@ - - - +

commit 5b33605469352f3403d44cb0d77ef3c224895f5b (HEAD, ichthyo/gui) @@ -25745,9 +25741,7 @@ - - - +

beim initialen Aufbau sitzen wir in einem ctor-Aufruf, @@ -26430,9 +26424,7 @@ - - - +

eigentlich brauchen wir den konkreten Typ nur für den ctor-Aufruf @@ -27618,9 +27610,7 @@ - - - +

...denn wir müssen sowiso einen globalen Pass machen, und zwar erst spät, wenn das Layout bereits komplett geregelt ist @@ -28094,9 +28084,7 @@ - - - +

gesucht... @@ -29404,9 +29392,7 @@ - - - +

...daher die Factory @@ -32479,9 +32465,7 @@ - - - +

herausfinden, warum er solid gezeichnet wird @@ -34171,9 +34155,7 @@ - - - +

Problem wegen dem Overview-Ruler, d.h. wie berücksichtigt der untere Canvas die Ausdehnung des oberen Canvas? @@ -35023,9 +35005,7 @@ - - - +

Zoomen, Scrollen, Scroll-Window und View-Path sind reine Gui-Bildungen und werden instantan ohne Round-Trip realisiert @@ -36266,9 +36246,7 @@ - - - +

g_application_activate() @@ -37121,9 +37099,7 @@ - - - +

nachdem sich eine Instanz einer Rolle gemeldet hat, kann der Hook sie individuell verknüpfen, typischerweise als Lambda @@ -37437,9 +37413,7 @@ - - - +

Warum möchte man denn überhaupt eine Geste "abbrechen können"? @@ -37670,9 +37644,7 @@ - - - +

da man verschiedene Fälle hier zu beginn gleich ausschließen kann @@ -55975,6 +55947,89 @@ + + + + + + + + + + + + + + + + + + + + + +

+ soll sowohl einfache Tyen, alsauch »strukturierte Typen« (tuple-like) akzeptieren +

+ + +
+ + + +

+ Konkret: man soll ein Binding auf eine Render-Node herstellen können von einer Funktion, die einfach einen Buffer-Pointer braucht. Aber aman soll auch Funktionen einbinden können, die zig verschiedene Eingabe- und Ausgabepuffer wollen, und dazu noch eine Menge an Parametern, die dann vielleicht auch noch per Automation zu versorgen sind +

+ +
+
+ + + + +

+ ...denn die weitere Node-Invocation beruht auf rekursiven Aufrufen in Vorgänger-Nodes, die dann jeweils ein BuffHandle abliefern. Sowas möchte man schmerzfrei in eine Art Array abstellen können (wenngleich es auch in der Praxis als UninitialisedStorage implementiert ist) +

+ +
+
+ + + + +

+ deshalb ist es sinnvoll, einen λ-closed Code-Block für jeden Index zu instantiieren +

+ + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + @@ -88484,7 +88539,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -88497,17 +88552,20 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + + - + - - + + + @@ -88592,13 +88650,42 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + +

+ will sagen ... das ist nicht das Problem; der Compiler macht hier eine verwirrende Transformation, die den Typ eines Funktionspointers mit Template-Argumenten instaniiert. Leider hat das aber zur Folge, daß man das eigentliche Problem nicht sieht (selbst wenn man endlos drauf starrt...) +

+ + +
- + + + + +

+ ...aber aufgrund der sonderbaren Zuordnung praktisch nicht zu erkennen.... +

+

+ Tip: wir übergeben einen extra int-Parameter an eine Funktion, die auf einen in&& parametrisiert wurde +

+ +
+ + + +
+
+ + - + + @@ -88615,8 +88702,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
Aufgerufen: (std::_Bind<void (*(std::_Placeholder<1>, int))(void*, int&&)>) (void*&)

- -
+
@@ -88656,13 +88742,32 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
Beobachtung: _Functor(_Bound_args ...)

+
+ + + + +

+ void (*(std::_Placeholder<1>, int))(void*, int&&) +

+
- + - + + + + +

+ (void (*)(void*, int&&)) (std::_Placeholder<1>, int) +

+ + +
+
@@ -88680,7 +88785,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -88739,8 +88844,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- -
+
@@ -88749,10 +88853,20 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + + + +

+ Ursache: Binder liefert eine Kopie +

+ +
+ + @@ -88768,8 +88882,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
also: func<ARGS& ...>

- - +
@@ -91751,6 +91864,46 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + + +

+ man könnte nun sagen: det sollen se halt nit machen! +

+ + +
+
+ + + + +

+ bedeutet, wir „biegen“ dann in einem Fall, nämlich wenn nur zwei Argumente gegeben sind, jeweils zwangsläufig in einer bestimmten Richtung ab, je nachdem ob eine leere Struct als wahr gilt; +

+

+ +

+

+ Beispielsweise in der aktuellen Logik prüfen wir ob der 1.Slot ein »Valu« ist, was dann bei einer leeren Struct dazu führen würde, diese als ein leeres Param-Tupel zu behandeln. Kommt dann darauf an, ob das tatsächlich einen Schaden im Code anrichtet, oder auch leer durchläuft. Denn an sich sollte es ja egal sein, sofern dann nur dieser Slot auch tatsächlich keine Behandlung bekommt +

+ + +
+ +
+ + + +
+
@@ -91890,9 +92043,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - + @@ -91955,7 +92108,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + + @@ -91965,10 +92120,10 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - - + + + + @@ -91987,11 +92142,11 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - - + + @@ -92053,15 +92208,141 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ Hut ab für den Poster auf Stackoverflow: der hat diese Möglichkeit gesehen und ist dann nur an ein paar Kleinigkeiten gescheitert. +

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

+ sonst dreht »man« (≙ich) noch durch... +

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

+ ...denn diese Mechanik ist im Grunde komplett unabhängig von Tuples (oder auch sonstigen Strukturen) +

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

+ lib::meta::forEachIDX<T> ( λ ) +

+ +
+ + + + + + +
+
+ + + + +
+ + + + - + - + + @@ -92078,9 +92359,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

MetaUtils_test::detect_tupleProtocol() @@ -92092,6 +92371,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + @@ -92160,7 +92442,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- +