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.
This commit is contained in:
Fischlurch 2025-02-17 21:18:37 +01:00
parent 0cad2dacc0
commit a5a3d46b6a
8 changed files with 186 additions and 78 deletions

View file

@ -699,9 +699,9 @@ namespace func{
typedef function<ReducedSig> ReducedFunc;
static ReducedFunc
reduced (SIG& f, Tuple<Types<X>> const& val)
reduced (SIG& f, X val)
{
Tuple<PreparedArgTypes> params {BuildPreparedArgs(val)};
Tuple<PreparedArgTypes> params {BuildPreparedArgs{std::forward_as_tuple (val)}};
return func::Apply<ARG_CNT>::template bind<ReducedFunc> (f, params);
}
};
@ -897,13 +897,10 @@ namespace func{
template<typename SIG, typename TERM>
inline
typename _PapE<SIG>::FunType::Functor
bindLast (SIG& f, TERM const& arg)
bindLast (SIG& f, TERM&& arg)
{
typedef Types<TERM> ArgTypeSeq;
typedef Tuple<ArgTypeSeq> ArgTuple;
ArgTuple argT(arg);
enum { LAST_POS = -1 + count<typename _Fun<SIG>::Args::List>::value };
return BindToArgument<SIG,TERM,LAST_POS>::reduced (f, argT);
return BindToArgument<SIG,TERM,LAST_POS>::reduced (f, std::forward<TERM> (arg));
}

View file

@ -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 <Ichthyostega@web.de>
@ -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<class PAR>
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<typename...VALS>
@ -69,7 +75,7 @@ namespace meta{
closeFront (VALS ...vs)
{
using ClosedTypes = TySeq<VALS...>;
return wrapBuilder (func::PApply<TupleBuilderSig, ClosedTypes>::bindFront (buildParam, std::make_tuple(vs...)));
return wrapBuilder (func::PApply<TupleBuilderSig, ClosedTypes>::bindFront (buildRecord, std::make_tuple(vs...)));
}
template<typename...VALS>
@ -77,7 +83,14 @@ namespace meta{
closeBack (VALS ...vs)
{
using ClosedTypes = TySeq<VALS...>;
return wrapBuilder (func::PApply<TupleBuilderSig, ClosedTypes>::bindBack (buildParam, std::make_tuple(vs...)));
return wrapBuilder (func::PApply<TupleBuilderSig, ClosedTypes>::bindBack (buildRecord, std::make_tuple(vs...)));
}
template<size_t idx, typename VAL>
static auto
close (VAL val)
{
return wrapBuilder (func::BindToArgument<TupleBuilderSig,VAL,idx>::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<typename...TTT>
struct ArrayAdapt;
/** Metafunction to detect if a type-sequence holds uniform types */
template<typename...TTT>
struct AllSame
: std::true_type // trivially true for empty conjunction and single element
{ };
template<typename T1, typename T2, typename...TS>
struct AllSame<T1,T2,TS...>
: __and_<is_same<T1,T2>
,AllSame<T2,TS...>
>
{ };
namespace {
template<typename...TTT>
struct AllSame
: std::true_type
{ };
template<typename T1, typename T2, typename...TS>
struct AllSame<T1,T2,TS...>
: __and_<is_same<T1,T2>
,AllSame<T2,TS...>
>
{ };
template<typename T, size_t N>
struct Repeat
{
using Rem = typename Repeat<T, N-1>::Seq;
using Seq = typename Prepend<T,Rem>::Seq;
};
template<typename T>
struct Repeat<T,0>
{
using Seq = TySeq<>;
};
/** Type constructor */
template<typename T, size_t N>
struct _Adapt
{
@ -141,6 +148,7 @@ namespace meta{
using _AdaptArray_t = typename _Adapt<T,N>::Array;
}
/** @note adding seamless conversion and compount-init */
template<typename T, typename...TT>
struct ArrayAdapt<T,TT...>
: std::array<T, 1+sizeof...(TT)>
@ -149,6 +157,7 @@ namespace meta{
,"Array can only hold elements of uniform type");
using Array = std::array<T, 1+sizeof...(TT)>;
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<typename T, size_t N>
struct TupleClosureBuilder<std::array<T,N>>
: TupleClosureBuilder<_AdaptArray_t<T,N>>
@ -182,6 +196,6 @@ namespace std { // Specialisation to support C++ »Tuple Protocol« and structur
: tuple_element<I, typename lib::meta::ArrayAdapt<TTT...>::Array>
{ };
// Note: the std::get<i> function will pick the subclass
// Note: the std::get<i> function will pick the baseclass anyway
}
#endif /*LIB_META_TUPLE_CLOSURE_H*/

View file

@ -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<class SRC, size_t...idx>
TupleConstructor (SRC values, IndexSeq<idx...>)
: Tuple<TYPES> (_ElmMapper_<SRC, Tuple<TYPES>, idx>{values}...)
TupleConstructor (SRC initVals, IndexSeq<idx...>)
: Tuple<TYPES> (_ElmMapper_<SRC, Tuple<TYPES>, idx>{initVals}...)
{ }
public:
template<class SRC>
TupleConstructor (SRC values)
: TupleConstructor (values, SequenceIterator())
: TupleConstructor (std::move(values), SequenceIterator())
{ }
};

View file

@ -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<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<class H, class T>
struct Types< Node<H,T> >
@ -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 TYPES, uint i=1>
class Shifted
{
@ -322,14 +322,14 @@ namespace meta {
template<class TYPES>
struct Shifted<TYPES,0>
{
{
typedef TYPES Type;
typedef typename Split<Type>::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<typename T, size_t N>
struct Repeat
{
using Rem = typename Repeat<T, N-1>::Seq;
using Seq = typename Prepend<T,Rem>::Seq;
};
template<typename T>
struct Repeat<T,0>
{
using Seq = TySeq<>;
};
}} // namespace lib::meta

View file

@ -206,9 +206,10 @@ namespace test {
void
build_Node_closedParam()
{
using Params = std::tuple<uint,uint,uint,uint,uint>;//array<uint, 5>;
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<uint, 5>;
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)

View file

@ -342,13 +342,12 @@ namespace test {
Sig15& f = fun15<1,2,3,4,5>;
SigA5& f5 = fun11<5>;
Tuple<Types<char>> argT(55);
function<SigR1> f_bound_1 = BindToArgument<Sig15,char,0>::reduced (f, argT);
function<SigR2> f_bound_2 = BindToArgument<Sig15,char,1>::reduced (f, argT);
function<SigR3> f_bound_3 = BindToArgument<Sig15,char,2>::reduced (f, argT);
function<SigR4> f_bound_4 = BindToArgument<Sig15,char,3>::reduced (f, argT);
function<SigR5> f_bound_5 = BindToArgument<Sig15,char,4>::reduced (f, argT);
function<SigR1> f_bound_1 = BindToArgument<Sig15,char,0>::reduced (f, 55);
function<SigR2> f_bound_2 = BindToArgument<Sig15,char,1>::reduced (f, 55);
function<SigR3> f_bound_3 = BindToArgument<Sig15,char,2>::reduced (f, 55);
function<SigR4> f_bound_4 = BindToArgument<Sig15,char,3>::reduced (f, 55);
function<SigR5> f_bound_5 = BindToArgument<Sig15,char,4>::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<Sig15> f_bound_X = BindToArgument<Sig15,char,5>::reduced (f, argT);
function<Sig15> f_bound_X = BindToArgument<Sig15,char,5>::reduced (f, 88);
CHECK (1+2+3+4+5 == f_bound_X (_1_,_2_,_3_,_4_,_5_) );

View file

@ -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<decltype(cons)>;
CHECK (FunType());
SHOW_EXPR(showType<FunType::Sig>())
CHECK (FunType() == true); // indeed a function
CHECK (showType<FunType::Sig>() == "tuple<int, double, string> (tuple<string>)"_expect);
Tup tup = cons("five");
@ -99,6 +99,26 @@ SHOW_EXPR(showType<FunType::Sig>())
}
/** @test fix specific argument within tuple
*/
void
tuple_bindArg()
{
using Tup = tuple<int,double,string>;
using Builder = TupleClosureBuilder<Tup>;
auto c1 = Builder::close<1>(3.1415927);
CHECK (showType<_Fun<decltype(c1)>::Sig>() == "tuple<int, double, string> (tuple<int, string>)"_expect);
Tup t1 = c1({2,"π"});
CHECK (t1 == "«tuple<int, double, string>»──(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<decltype(c2)>::Sig>() == "tuple<int, double, string> (tuple<int, double, string>)"_expect);
}
/** @test use a std::array and handle it like a tuple to pre-fix some elements
*/
void

View file

@ -106027,12 +106027,65 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
<node COLOR="#338800" CREATED="1739752776321" ID="ID_376565948" MODIFIED="1739752785816" TEXT="TupleClosure_test zur Dokumentation">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1739822405519" ID="ID_1159986625" MODIFIED="1739822418374" TEXT="weitere partial-closure-F&#xe4;lle integrieren">
<icon BUILTIN="button_ok"/>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1739822430227" ID="ID_1498733913" MODIFIED="1739822453922">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
wie steht's mit <i>perfect forwarding</i>?
</p>
</body>
</html>
</richcontent>
<font NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="help"/>
<node CREATED="1739822460457" ID="ID_813032583" MODIFIED="1739822493962">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
ohnehin limitiert: wir konstruieren stets Werte <i>in den Binder</i>
</p>
</body>
</html>
</richcontent>
<icon BUILTIN="idea"/>
</node>
<node COLOR="#5b280f" CREATED="1739822514329" ID="ID_304319384" MODIFIED="1739822915159" TEXT="TupleConstructor?">
<icon BUILTIN="help"/>
<icon BUILTIN="button_cancel"/>
<node CREATED="1739822603555" ID="ID_1985055743" MODIFIED="1739822681557" TEXT="er nimmt ein Argument-Tupel per Value"/>
<node CREATED="1739822682873" ID="ID_76296788" MODIFIED="1739822813779" TEXT="dessen Inhalt wird per Referenz durch den ElmMapper durchgereicht"/>
<node CREATED="1739822742383" ID="ID_1631025117" MODIFIED="1739822805415" TEXT="das ist hier sogar ein essentielles Feature">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
...denn nur auf diesem Weg hat der ElmMapper die komplette Freiheit und kann im Besonderen Werte selbst konstruieren, oder aber auch Werte mehrfach mappen
</p>
</body>
</html>
</richcontent>
</node>
</node>
<node CREATED="1739822955768" ID="ID_82653424" MODIFIED="1739822974789" TEXT="wir k&#xf6;nnen also nur Werte bis zum Tuple-Constructor bringen"/>
<node CREATED="1739822976729" ID="ID_539739884" MODIFIED="1739822995033" TEXT="aber: BindToArgument k&#xf6;nnte einen einfachen Wert nehmen"/>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1739756728914" ID="ID_1671606956" MODIFIED="1739813490672" TEXT="Auf std::array anwendbar machen">
<icon BUILTIN="pencil"/>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1739756741667" ID="ID_20824263" MODIFIED="1739813448142" TEXT="Problem: spezielle Typsignatur">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1739756741667" ID="ID_20824263" MODIFIED="1739816589487" TEXT="Problem: spezielle Typsignatur">
<icon BUILTIN="button_ok"/>
<node CREATED="1739756760373" ID="ID_753184722" MODIFIED="1739756775186" TEXT="deshalb ist in jedem Fall eine partielle Spezialisierung notwendig"/>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1739756922215" ID="ID_1294911921" MODIFIED="1739798944791" TEXT="Problem: eine Builder-Funktion mit expliziter Argument-Anzahl">
<node BACKGROUND_COLOR="#edc89e" COLOR="#690f14" CREATED="1739756922215" ID="ID_1294911921" MODIFIED="1739816544790" TEXT="Problem: eine Builder-Funktion mit expliziter Argument-Anzahl">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1739756977184" ID="ID_1621203501" MODIFIED="1739757006679" TEXT="den Signatur-Typ k&#xf6;nnte man noch leicht synthetisieren">
<richcontent TYPE="NOTE"><html>
@ -106049,16 +106102,24 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
<icon BUILTIN="button_cancel"/>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1739798967457" ID="ID_1274106457" MODIFIED="1739813473419" TEXT="anderer Ansatz: Tupel-Typsignatur emulieren">
<node BACKGROUND_COLOR="#e3dfc5" COLOR="#435e98" CREATED="1739798967457" ID="ID_1274106457" MODIFIED="1739816566683" TEXT="anderer Ansatz: Tupel-Typsignatur emulieren">
<icon BUILTIN="idea"/>
<node CREATED="1739798985823" ID="ID_9547449" MODIFIED="1739799013054" TEXT="...dann k&#xf6;nnte die Standard-Impl verwendet werden"/>
<node CREATED="1739799014639" ID="ID_205042889" MODIFIED="1739799031581" TEXT="brauche daf&#xfc;r einen automatisch konvertierbaren Adapter"/>
<node CREATED="1739807307000" ID="ID_382558613" MODIFIED="1739812304265" TEXT="mu&#xdf; dann wohl Tuple-Protocol reimplementieren">
<node COLOR="#435e98" CREATED="1739799014639" ID="ID_205042889" MODIFIED="1739816580797" TEXT="brauche daf&#xfc;r einen automatisch konvertierbaren Adapter">
<icon BUILTIN="yes"/>
</node>
<node COLOR="#435e98" CREATED="1739807307000" ID="ID_382558613" MODIFIED="1739816571751" TEXT="mu&#xdf; dann wohl Tuple-Protocol reimplementieren">
<arrowlink COLOR="#846867" DESTINATION="ID_872833596" ENDARROW="Default" ENDINCLINATION="-1112;-50;" ID="Arrow_ID_577510339" STARTARROW="None" STARTINCLINATION="-1320;72;"/>
<icon BUILTIN="smiley-oh"/>
</node>
</node>
</node>
<node COLOR="#338800" CREATED="1739816632434" ID="ID_1046144111" MODIFIED="1739816647386" TEXT="Aggregat-Initialisierung erm&#xf6;glichen">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1739816653616" ID="ID_1737045068" MODIFIED="1739816676602" TEXT="Tuple-Protocol wird unterst&#xfc;tzt">
<icon BUILTIN="flag-pink"/>
</node>
</node>
</node>
</node>
@ -154631,8 +154692,7 @@ std::cout &lt;&lt; tmpl.render({&quot;what&quot;, &quot;World&quot;}) &lt;&lt; s
Angenommen, wir haben eine Klasse, die aus std::tuple ableitet. Man w&#252;rde sich w&#252;nschen, diese w&#252;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&#228;re man auf die Implementierung von std::tuple festgelegt, und k&#246;nnte z.B. keine Anpassungen am Storage-Management machen. Wenn man <i>so etwas m&#246;chte, dann sollte man die klassische-OO verwenden.</i>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1739807542287" ID="ID_1950397403" MODIFIED="1739807563117" TEXT="Vorsicht Falle: Funktion-Overloads beachten die Basisklasse">
<icon BUILTIN="messagebox_warning"/>
@ -154684,8 +154744,7 @@ std::cout &lt;&lt; tmpl.render({&quot;what&quot;, &quot;World&quot;}) &lt;&lt; s
ABER nur wenn der Template-Parameter <b>f&#252;r diesen konkreten Aufruf</b>&#160; instantiiert wird; ein Typ-Parameter einer umschlie&#223;enden Klasse gilt bereits als fest gebunden, also <i>funktioniert mit damit kein &#187;perfect forwarding&#171;.</i>&#160;Im Besonderen bei Konstruktoren ist das eine h&#228;ufig anzutreffende Falle
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1739804952757" ID="ID_996711039" MODIFIED="1739804967987" TEXT="es ist und bleibt ein Compilerbau-Trick">
<icon BUILTIN="smiley-angry"/>