From 990e4cbb6862fa0bbd548ddbb0d1c3f29c06872c Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 18 Dec 2024 20:50:33 +0100 Subject: [PATCH] Invocation: simplify and unify type decision logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now reaping the benefits of the ambitious refactorings done yesterday. - Only retaining the basic distinction of the four use cases - all further adaptation now directly based on the »lifted« types - can even add quite stringent compile-time sanity checks. Now the refactoring is on-par with the capabilities of the old downstream code, which, btw, could be retained in compilable (yet not working) state. But the new traits logic is already more capable and could accept tuples and arrays as well. Next major topic to address is to provide the foundation for parameter handling. --- src/lib/meta/util.hpp | 6 + src/steam/engine/feed-manifold.hpp | 195 ++++++++++------------------- wiki/thinkPad.ichthyo.mm | 164 ++++++++++++++++-------- 3 files changed, 180 insertions(+), 185 deletions(-) diff --git a/src/lib/meta/util.hpp b/src/lib/meta/util.hpp index 4a9c12425..24849ce6a 100644 --- a/src/lib/meta/util.hpp +++ b/src/lib/meta/util.hpp @@ -270,6 +270,12 @@ namespace meta { using BAS::BAS; }; + template + struct Tagged + : BAS + { + using BAS::BAS; + }; diff --git a/src/steam/engine/feed-manifold.hpp b/src/steam/engine/feed-manifold.hpp index bf9451c95..43310b4f7 100644 --- a/src/steam/engine/feed-manifold.hpp +++ b/src/steam/engine/feed-manifold.hpp @@ -106,21 +106,20 @@ namespace engine { namespace {// Introspection helpers.... using lib::meta::_Fun; + using lib::meta::enable_if; using lib::meta::is_UnaryFun; using lib::meta::is_BinaryFun; using lib::meta::is_TernaryFun; - using std::remove_reference_t; - using lib::meta::enable_if; using lib::meta::is_Structured; using lib::meta::forEachIDX; using lib::meta::ElmTypes; + using lib::meta::Tagged; using lib::meta::TySeq; using std::is_pointer; using std::is_reference; + using std::remove_reference_t; using std::remove_pointer_t; using std::tuple_element_t; - using std::tuple_size_v; - using std::void_t; using std::__and_; using std::__not_; @@ -129,7 +128,6 @@ namespace engine { struct is_Value : __and_<__not_> ,__not_> - ,__not_> ,std::is_default_constructible ,std::is_copy_assignable > @@ -146,115 +144,49 @@ namespace engine { - 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 - { - using Seq = TySeq; - using Tup = std::tuple; - }; - - template - struct StructType> > - { - 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 - * is done by a function, which takes an array of input and output - * buffer pointers with a common type; this simple case is used - * 7/2024 for prototyping and validate the design. - * @tparam FUN a _function-like_ object, expected to accept two arguments, - * which both are arrays of buffer pointers (input, output). + /** + * Trait template to analyse and adapt to the given processing function. + * The detection logic encoded here attempts to figure out the meaning of + * the function arguments by their arrangement and type. As a base rule, + * the arguments are expected in the order: Parameters, Input, Output + * - a single argument function can only be a data generator + * - a binary function can either be a processor input -> output, + * or accept parameters at «slot-0» and provide output at «slot-1» + * - a ternary function is expected to accept Parameters, Input, Output. + * @tparam FUN a _function-like_ object, expected to accept 1 - 3 arguments, + * which all may be simple types, tuples or arrays. + * @note »Buffers« are always accepted by pointer, which allows + * to distinguish parameter and data «slots« */ template struct _ProcFun { - static_assert(_Fun() , "something funktion-like required"); + static_assert(_Fun() , "something funktion-like required"); static_assert(0 < _Fun::ARITY , "function with at least one argument expected"); static_assert(3 >= _Fun::ARITY , "function with up to three arguments accepted"); using Sig = typename _Fun::Sig; - template - using _Arg = typename lib::meta::Pick::Args, i>::Type; - - - template - struct _ArgTrait - { - static_assert(not sizeof(ARG), "processing function expected to take parameters, " - "buffer-poiinters or tuples or arrays of these"); - }; - template - struct _ArgTrait>> - { - using Buff = PAR; ////////////////////////////////OOO not correct, remove this! - using Args = TySeq; - enum{ SIZ = 1 }; - }; - template - 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 }; - }; + template + using _Arg = typename lib::meta::Pick::Args, i>::Type; + template class COND> + using AllElements = typename ElmTypes<_Arg>::template AndAll; + template + static constexpr bool nonEmpty = ElmTypes<_Arg>::SIZ; + template + static constexpr bool is_BuffSlot = AllElements(); + template + static constexpr bool is_ParamSlot = AllElements(); + + /** + * Detect use-case as indicated by the function signature + */ template struct _Case { - static_assert(not sizeof(SIG), "use case could not be determined from function stignature"); + static_assert (not sizeof(SIG), "use case could not be determined from function signature"); }; template struct _Case>> @@ -267,7 +199,7 @@ namespace engine { struct _Case>> { enum{ SLOT_O = 1 - , SLOT_I = typename ElmTypes<_Arg>::template AndAll()? 0 : 1 + , SLOT_I = (nonEmpty<0> and is_BuffSlot<0>)? 0 : 1 }; }; template @@ -278,19 +210,16 @@ 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 SigI = _Arg<_Case::SLOT_I>; + using SigO = _Arg<_Case::SLOT_O>; + using SigP = _Arg< 0>; + using ArgI = typename ElmTypes::Seq; + using ArgO = typename ElmTypes::Seq; + using ArgP = typename ElmTypes::Seq; - using BuffI = typename _ArgTrait::Buff; - using BuffO = typename _ArgTrait::Buff; - - enum{ FAN_I = _ArgTrait::SIZ - , FAN_O = _ArgTrait::SIZ - , FAN_P = _ArgTrait::SIZ + enum{ FAN_I = ElmTypes::SIZ + , FAN_O = ElmTypes::SIZ + , FAN_P = ElmTypes::SIZ , SLOT_I = _Case::SLOT_I , SLOT_O = _Case::SLOT_O , SLOT_P = 0 @@ -299,9 +228,22 @@ namespace engine { static constexpr bool hasInput() { return SLOT_I != SLOT_O; } static constexpr bool hasParam() { return 0 < SLOT_I; } + + /* ========== |consistency checks| ========== */ + static_assert (nonEmpty or nonEmpty or nonEmpty + ,"At least one slot of the function must accept data"); + static_assert (is_BuffSlot, "Output slot of the function must accept buffer pointers"); + static_assert (is_BuffSlot, "Input slot of the function must accept buffer pointers"); + static_assert (is_ParamSlot or not hasParam() + ,"Param slot must accept value data"); + + using BuffO = typename ArgO::List::Head; + using BuffI = typename std::conditional::type; /////////////////////////TODO obsolete ... remove after switch }; + + /** FeedManifold building block: hold parameter data */ template struct ParamStorage { @@ -330,7 +272,7 @@ namespace engine { }; template - struct NotProvided : lib::meta::NullType { }; + using NotProvided = Tagged; template using Provide_if = std::conditional_t>; @@ -394,10 +336,6 @@ namespace engine { : process{forward (funSetup)...} { } - - using TupI = typename StructType::Tup; - using TupO = typename StructType::Tup; - template auto& accessArg (ARG& arg) @@ -408,6 +346,10 @@ namespace engine { return arg; } + using TupI = typename ElmTypes::Tup; + using TupO = typename ElmTypes::Tup; + + void connect() { @@ -418,11 +360,6 @@ namespace engine { 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) { @@ -431,11 +368,6 @@ namespace engine { 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(); } } @@ -465,8 +397,8 @@ namespace engine { struct SimpleFunctionInvocationAdapter : MAN { - using BuffI = typename _ProcFun::BuffI; - using BuffO = typename _ProcFun::BuffO; + using BuffI = remove_pointer_t::BuffI>; + using BuffO = remove_pointer_t::BuffO>; enum{ N = MAN::STORAGE_SIZ , FAN_I = _ProcFun::FAN_I @@ -475,7 +407,8 @@ namespace engine { static_assert(FAN_I <= N and FAN_O <= N); - using ArrayI = typename _ProcFun::SigI; +// using ArrayI = typename _ProcFun::SigI; + using ArrayI = typename _Fun::Args::List::Head; ///////////////////TODO workaround for obsolete code, about to be removed using ArrayO = typename _ProcFun::SigO; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index d1ae8f6df..260cc0b14 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -25432,9 +25432,7 @@ - - - +

sub-Frame wird für unsere Kinder erzeugt @@ -25776,9 +25774,7 @@ - - - +

inwiefern gibt es Beschränkungen, wenn man ein Kind-Widget von einem Container entfernt? @@ -26076,9 +26072,7 @@ - - - +

also einen speziellen Offset für Clips und einen anderen speziellen Offset für Effekt-Marker? @@ -26483,9 +26477,7 @@ - - - +

Der Lösungsansatz ist entschieden... @@ -27153,9 +27145,7 @@ - - - +

⟹ ich kann das Ergebnis nicht exportieren @@ -28370,9 +28360,7 @@ - - - +

weil der Ruler ja in die Präsentation mit einbezogen ist @@ -30375,9 +30363,7 @@ - - - +

Eigentlich benötigt wird das »Aufspreizen« nur in der vertikalen Dimension, damit sich die umschließende Box sinngemäß anpaßt; im Grunde würde es sogar genügen, nur das obere (Ruler)-ScrolledWindow zu dimensionieren, aber ich halte es für sicherer, vom eigentlichen innen liegenden Canvas aus aufzuspreizen, schon wegen der ggfs. dynamischen Dekoration für die Scrollbar. Als Kompromiß setze ich jetzt horizontal eine Mindest-Ausdehnung von 100px (das erscheint ohnehin sinnvoll für eine Timeline), aber in vertikaler Richtung setze ich einen size-Request auf die berechnete Canvas-Höhe @@ -31400,9 +31386,7 @@ - - - +

es ruft den Konstruktor WidgetPath(gobject, make_copy=true) auf @@ -33862,9 +33846,7 @@ - - - +

das war ein größeres Refactoring; dafür fällt dann die Lösung mit den rekursiv "eingehäkelten" Lambdas weg. Ist sicherlich besser so... @@ -35218,9 +35200,7 @@ - - - +

Ich will nicht, daß der DisplayManager zu bedeutend wird, weil dann eine direkte Manipulation einzelner Widgets durch den DisplayManager als die "einfachste" und "natürlichste" Lösung erscheinen könnte. Dagegen wehre ich mich, weil es zu einer starken Kopplung führt. @@ -36098,9 +36078,7 @@ - - - +

betrifft aber nur Framework-Funktionalität @@ -36187,9 +36165,7 @@ - - - +

The signal_activate() signal is emitted on the primary instance @@ -36597,9 +36573,7 @@ - - - +

aber wir brauchen ein laufendes UI @@ -37064,9 +37038,7 @@ - - - +

also nicht generisch, sondern die spezifische Closure für z.B. den Fall drag-Clip @@ -91737,7 +91709,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -91830,7 +91802,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -91844,6 +91816,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ +
@@ -91882,7 +91856,29 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + +

+ Erläuterung: egal wie die interne Logik abläuft, am Ende muß sie für jeden Slot den Typ (kompatibel) ausweisen, der initial für diese Stelle in der Signatur angegeben wurde +

+ + +
+ +
+
+ + + + + + + @@ -91893,6 +91889,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ @@ -92210,11 +92207,11 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - + @@ -92222,9 +92219,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - + + + @@ -92378,17 +92375,17 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - + - - + + @@ -92442,11 +92439,24 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + + + + + + + + + + + + + @@ -92457,6 +92467,52 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + +

+ void dummyOp (NoArg in, SoloArg out) +

+ + +
+ +
+ + + + +

+ rein logisch läßt sich das nicht lösen — die Entscheidung beruht dann auf einer willkürlichen Konvention; im Moment habe ich fesgelegt: »das können keine Buffer sein« +

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

+ denn für einen Parameter-Slot muß weniger getan werden; im Zweifelsfall muß er nur default-konstruiert werden — und es wird dann halt auch kein Param-Funktor dazu aufgerufen +

+ +
+
+ + + + + +
+