From a5a3d46b6ab8fb3909ee4a6a271af6c55b247c06 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 17 Feb 2025 21:18:37 +0100 Subject: [PATCH] Invocation: generalise partial-closure cases With these additions, all conceivable cases are basically addressed. Take this as opportunity to investigate how the existing implementation transports values into the Binder, where they will be stored as data fields. Notably the mechanism of the `TupleConstructor` / `ElmMapper` indeed ''essentially requires'' to pass the initialisers ''by-reference'', because otherwise there would be limitations on possible mappings. This implies that not much can be done for ''perfect forwarding'' of initialisers, but at least the `BindToArgument` can be simplified to take the value directly. --- src/lib/meta/function-closure.hpp | 11 +-- src/lib/meta/tuple-closure.hpp | 80 +++++++++++-------- src/lib/meta/tuple-helper.hpp | 15 ++-- src/lib/meta/typeseq-util.hpp | 35 +++++--- tests/core/steam/engine/node-builder-test.cpp | 7 +- .../meta/function-composition-test.cpp | 13 ++- tests/library/meta/tuple-closure-test.cpp | 24 +++++- wiki/thinkPad.ichthyo.mm | 79 +++++++++++++++--- 8 files changed, 186 insertions(+), 78 deletions(-) diff --git a/src/lib/meta/function-closure.hpp b/src/lib/meta/function-closure.hpp index ed2451574..0c1764e6e 100644 --- a/src/lib/meta/function-closure.hpp +++ b/src/lib/meta/function-closure.hpp @@ -699,9 +699,9 @@ namespace func{ typedef function ReducedFunc; static ReducedFunc - reduced (SIG& f, Tuple> const& val) + reduced (SIG& f, X val) { - Tuple params {BuildPreparedArgs(val)}; + Tuple params {BuildPreparedArgs{std::forward_as_tuple (val)}}; return func::Apply::template bind (f, params); } }; @@ -897,13 +897,10 @@ namespace func{ template inline typename _PapE::FunType::Functor - bindLast (SIG& f, TERM const& arg) + bindLast (SIG& f, TERM&& arg) { - typedef Types ArgTypeSeq; - typedef Tuple ArgTuple; - ArgTuple argT(arg); enum { LAST_POS = -1 + count::Args::List>::value }; - return BindToArgument::reduced (f, argT); + return BindToArgument::reduced (f, std::forward (arg)); } diff --git a/src/lib/meta/tuple-closure.hpp b/src/lib/meta/tuple-closure.hpp index a0fba6cb1..3f28e210b 100644 --- a/src/lib/meta/tuple-closure.hpp +++ b/src/lib/meta/tuple-closure.hpp @@ -1,5 +1,5 @@ /* - TUPLE-CLOSURE.hpp - metaprogramming tools for closing a function over given arguments + TUPLE-CLOSURE.hpp - metaprogramming tools for tuple-likes with partial closure Copyright (C) 2025, Hermann Vosseler @@ -49,6 +49,12 @@ namespace lib { namespace meta{ + /** + * Metaprogramming helper to build a constructor-function + * for »tuple-like« records, where some of the initialisation + * values are immediately closed (≙ fixed), while the remaining + * ones are supplied as function arguments. + */ template struct TupleClosureBuilder; @@ -59,9 +65,9 @@ namespace meta{ using TupleBuilderSig = Tuple(PARS...); static Tuple - buildParam (PARS ...params) + buildRecord (PARS ...params) { - return {params...}; + return {std::move(params)...}; } template @@ -69,7 +75,7 @@ namespace meta{ closeFront (VALS ...vs) { using ClosedTypes = TySeq; - return wrapBuilder (func::PApply::bindFront (buildParam, std::make_tuple(vs...))); + return wrapBuilder (func::PApply::bindFront (buildRecord, std::make_tuple(vs...))); } template @@ -77,7 +83,14 @@ namespace meta{ closeBack (VALS ...vs) { using ClosedTypes = TySeq; - return wrapBuilder (func::PApply::bindBack (buildParam, std::make_tuple(vs...))); + return wrapBuilder (func::PApply::bindBack (buildRecord, std::make_tuple(vs...))); + } + + template + static auto + close (VAL val) + { + return wrapBuilder (func::BindToArgument::reduced (buildRecord, val)); } private: @@ -97,39 +110,33 @@ namespace meta{ }; + /* ===== adapt array for tuple-like signature ===== */ + /** + * Metaprogramming adapter to overlay a tuple-like signature + * on top of std::array, with N times the same type. + */ template struct ArrayAdapt; + + /** Metafunction to detect if a type-sequence holds uniform types */ + template + struct AllSame + : std::true_type // trivially true for empty conjunction and single element + { }; + + template + struct AllSame + : __and_ + ,AllSame + > + { }; + + namespace { - - template - struct AllSame - : std::true_type - { }; - - template - struct AllSame - : __and_ - ,AllSame - > - { }; - - - template - struct Repeat - { - using Rem = typename Repeat::Seq; - using Seq = typename Prepend::Seq; - }; - - template - struct Repeat - { - using Seq = TySeq<>; - }; - + /** Type constructor */ template struct _Adapt { @@ -141,6 +148,7 @@ namespace meta{ using _AdaptArray_t = typename _Adapt::Array; } + /** @note adding seamless conversion and compount-init */ template struct ArrayAdapt : std::array @@ -149,6 +157,7 @@ namespace meta{ ,"Array can only hold elements of uniform type"); using Array = std::array; + ArrayAdapt (Array const& o) : Array{o} { } ArrayAdapt (Array && r) : Array{move(r)} { } @@ -158,6 +167,11 @@ namespace meta{ { } }; + + /** partial specialisation to handle a std::array. + * @note the expected input on partially closures + * is then also an array, with fewer elements. + */ template struct TupleClosureBuilder> : TupleClosureBuilder<_AdaptArray_t> @@ -182,6 +196,6 @@ namespace std { // Specialisation to support C++ »Tuple Protocol« and structur : tuple_element::Array> { }; - // Note: the std::get function will pick the subclass + // Note: the std::get function will pick the baseclass anyway } #endif /*LIB_META_TUPLE_CLOSURE_H*/ diff --git a/src/lib/meta/tuple-helper.hpp b/src/lib/meta/tuple-helper.hpp index 02b32993d..e9d69a814 100644 --- a/src/lib/meta/tuple-helper.hpp +++ b/src/lib/meta/tuple-helper.hpp @@ -225,11 +225,12 @@ namespace meta { * - the actual index position of the tuple element * to be initialised through this concrete instantiation. * @remarks this design has several extension points. Pretty much any conceivable - * initialisation logic can be embodied in the `_ElmMapper_` template. The sole - * requirement is that the concrete instance is _assignable_ by the source type + * initialisation logic can be embodied in the `_ElmMapper_` template. + * Required is that the concrete instance is _constructible_ from the source type * and _convertible_ to the individual member type of the target tuple it is - * invoked for. Moreover, it is possible to build a generic _element extractor_, - * which will be specialised on base of the source type accepted. + * invoked for. Source data _must_ be taken by-value, from the ctor argument. + * @note based on this mechanics, a generic _element extractor_ may be built, + * selecting a (partial) specialisation based on the source type given. * @see ExtractArg */ template< typename TYPES @@ -243,15 +244,15 @@ namespace meta { protected: template - TupleConstructor (SRC values, IndexSeq) - : Tuple (_ElmMapper_, idx>{values}...) + TupleConstructor (SRC initVals, IndexSeq) + : Tuple (_ElmMapper_, idx>{initVals}...) { } public: template TupleConstructor (SRC values) - : TupleConstructor (values, SequenceIterator()) + : TupleConstructor (std::move(values), SequenceIterator()) { } }; diff --git a/src/lib/meta/typeseq-util.hpp b/src/lib/meta/typeseq-util.hpp index d8a8af1e4..08e9e1690 100644 --- a/src/lib/meta/typeseq-util.hpp +++ b/src/lib/meta/typeseq-util.hpp @@ -80,9 +80,9 @@ namespace meta { } - /** + /** * Helper: prepend a type to an existing type sequence, - * thus shifting all elements within the sequence + * thus shifting all elements within the sequence * to the right, eventually dropping the last element * @todo support variadic type-seq ////////////////////////////////////////////////////////////////TICKET #987 : make lib::meta::Types variadic, then replace this by a single variadic template */ @@ -128,9 +128,9 @@ namespace meta { - /** + /** * Additional specialisation of the basic type sequence type, - * allowing to re-create a (flat) type sequence from a typelist. + * allowing to re-create a (flat) type sequence from a typelist. */ template struct Types< Node > @@ -280,7 +280,7 @@ namespace meta { , T16,T17,T18,T19,T20 > > { - typedef typename + typedef typename Types< T01,T02,T03,T04,T05 , T06,T07,T08,T09,T10 , T11,T12,T13,T14,T15 @@ -308,9 +308,9 @@ namespace meta { /** - * Helper: generate a type sequence left shifted + * Helper: generate a type sequence left shifted * by i steps, filling in NullType at the end - */ + */ template class Shifted { @@ -322,14 +322,14 @@ namespace meta { template struct Shifted - { + { typedef TYPES Type; typedef typename Split::Head Head; }; - /** + /** * specialisation: pick n-th element from a type sequence * @see typelist-manip.hpp */ @@ -346,6 +346,23 @@ namespace meta { + /** + * Generate a type-sequence filled with + * \a N times the same type \a T + */ + template + struct Repeat + { + using Rem = typename Repeat::Seq; + using Seq = typename Prepend::Seq; + }; + + template + struct Repeat + { + using Seq = TySeq<>; + }; + }} // namespace lib::meta diff --git a/tests/core/steam/engine/node-builder-test.cpp b/tests/core/steam/engine/node-builder-test.cpp index 26e201706..90d94fba0 100644 --- a/tests/core/steam/engine/node-builder-test.cpp +++ b/tests/core/steam/engine/node-builder-test.cpp @@ -206,9 +206,10 @@ namespace test { void build_Node_closedParam() { - using Params = std::tuple;//array; - auto procFun = [](Params params, uint* out) { auto [v1,v2,v3,v4,v5] = params; *out = v1+v2+v3+v4+v5; };//explore(params).resultSum(); }; - auto autoFun = [](Time nomTime){ return FrameNr::quant (nomTime, SECONDS_GRID); }; + using Params = array; + auto procFun = [](Params params, uint* out){ *out = explore(params).resultSum(); }; + auto autoFun = [](Time nomTime){ return uint(FrameNr::quant (nomTime, SECONDS_GRID));}; + ProcNode node{prepareNode("Test") .preparePort() .invoke ("fun()", procFun) diff --git a/tests/library/meta/function-composition-test.cpp b/tests/library/meta/function-composition-test.cpp index 335747057..e63872159 100644 --- a/tests/library/meta/function-composition-test.cpp +++ b/tests/library/meta/function-composition-test.cpp @@ -342,13 +342,12 @@ namespace test { Sig15& f = fun15<1,2,3,4,5>; SigA5& f5 = fun11<5>; - Tuple> argT(55); - function f_bound_1 = BindToArgument::reduced (f, argT); - function f_bound_2 = BindToArgument::reduced (f, argT); - function f_bound_3 = BindToArgument::reduced (f, argT); - function f_bound_4 = BindToArgument::reduced (f, argT); - function f_bound_5 = BindToArgument::reduced (f, argT); + function f_bound_1 = BindToArgument::reduced (f, 55); + function f_bound_2 = BindToArgument::reduced (f, 55); + function f_bound_3 = BindToArgument::reduced (f, 55); + function f_bound_4 = BindToArgument::reduced (f, 55); + function f_bound_5 = BindToArgument::reduced (f, 55); CHECK (55+2+3+4+5 == f_bound_1 ( _2_,_3_,_4_,_5_) ); CHECK (1+55+3+4+5 == f_bound_2 (_1_, _3_,_4_,_5_) ); @@ -359,7 +358,7 @@ namespace test { // degenerate case: specify wrong argument position (behind end of argument list) // causes the argument to be simply ignored and no binding to happen - function f_bound_X = BindToArgument::reduced (f, argT); + function f_bound_X = BindToArgument::reduced (f, 88); CHECK (1+2+3+4+5 == f_bound_X (_1_,_2_,_3_,_4_,_5_) ); diff --git a/tests/library/meta/tuple-closure-test.cpp b/tests/library/meta/tuple-closure-test.cpp index 2db0d019c..cff1245d0 100644 --- a/tests/library/meta/tuple-closure-test.cpp +++ b/tests/library/meta/tuple-closure-test.cpp @@ -55,6 +55,7 @@ namespace test { { tuple_bindFront(); tuple_bindBack(); + tuple_bindArg(); array_bindFront(); } @@ -69,8 +70,7 @@ namespace test { auto cons = Builder::closeFront (1,2.3); using FunType = _Fun; - CHECK (FunType()); -SHOW_EXPR(showType()) + CHECK (FunType() == true); // indeed a function CHECK (showType() == "tuple (tuple)"_expect); Tup tup = cons("five"); @@ -99,6 +99,26 @@ SHOW_EXPR(showType()) } + /** @test fix specific argument within tuple + */ + void + tuple_bindArg() + { + using Tup = tuple; + using Builder = TupleClosureBuilder; + + auto c1 = Builder::close<1>(3.1415927); + CHECK (showType<_Fun::Sig>() == "tuple (tuple)"_expect); + + Tup t1 = c1({2,"π"}); + CHECK (t1 == "«tuple»──(2,3.1415927,π)"_expect); + + auto c2 = Builder::close<3>("fantastic"); + // Binding to out-of-scope arguments is ignored: the result is the identity-function + CHECK (showType<_Fun::Sig>() == "tuple (tuple)"_expect); + } + + /** @test use a std::array and handle it like a tuple to pre-fix some elements */ void diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index d6a231fb0..768b21ad6 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -106027,12 +106027,65 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + + + + + + +

+ wie steht's mit perfect forwarding? +

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

+ ohnehin limitiert: wir konstruieren stets Werte in den Binder +

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

+ ...denn nur auf diesem Weg hat der ElmMapper die komplette Freiheit und kann im Besonderen Werte selbst konstruieren, oder aber auch Werte mehrfach mappen +

+ + +
+
+
+ + + + - - + + - + @@ -106049,16 +106102,24 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + - - + + + + + + + + + + @@ -154631,8 +154692,7 @@ std::cout << tmpl.render({"what", "World"}) << s Angenommen, wir haben eine Klasse, die aus std::tuple ableitet. Man würde sich wünschen, diese würde automatisch am Tuple-Protocol partizipieren. Tut sie aber nicht, weil schon die Spezialisierung von std::tuple_size nicht greift. Das ist aber auch gut so, denn sonst wäre man auf die Implementierung von std::tuple festgelegt, und könnte z.B. keine Anpassungen am Storage-Management machen. Wenn man so etwas möchte, dann sollte man die klassische-OO verwenden.

- - +
@@ -154684,8 +154744,7 @@ std::cout << tmpl.render({"what", "World"}) << s ABER nur wenn der Template-Parameter für diesen konkreten Aufruf  instantiiert wird; ein Typ-Parameter einer umschließenden Klasse gilt bereits als fest gebunden, also funktioniert mit damit kein »perfect forwarding«. Im Besonderen bei Konstruktoren ist das eine häufig anzutreffende Falle

- - +