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

- - +