Invocation / Library: analyse perfect-forwarding in function-closure

__Summary__:
 * the first part to prepare a binding involves creating a mapped tuple,
   with re-ordered elements and some elements replaced by placehoder-markers.
   This part **must not use RValue-References** (doing so would be possible
   only under very controlled conditions)
 * the second part, which transports these mapped-tuple elements into the binder
   ''could be converted to perfect-forwarding.'' This would require to replace
   the `Apply<N>' by a variadic template, delegating to `std::apply` and `std::bind`

With this changeset, I have modernised a lot of typedefs to make them more legible,
and I have introduced perfect-forwarding in the entrance path, up to the point
where the values are passed to `TupleConstructor`.
This commit is contained in:
Fischlurch 2025-02-17 23:34:06 +01:00
parent a5a3d46b6a
commit 89f839854c
3 changed files with 202 additions and 61 deletions

View file

@ -83,6 +83,10 @@ namespace func{
* this Helper with repetitive specialisations for up to nine arguments
* is used either to apply a function to arguments given as a tuple, or
* to create the actual closure (functor) over all function arguments.
* @todo 2/2025 should be replaced by a single variadic template, and
* implemented using std::apply. Note also that std::bind would
* support perfect-forwarding, especially also for the functor;
* the latter would allow to use move-only functors.
*/
template<uint n>
struct Apply;
@ -567,11 +571,11 @@ namespace func{
template<typename SIG, typename VAL>
class PApply
{
typedef typename _Fun<SIG>::Args Args;
typedef typename _Fun<SIG>::Ret Ret;
typedef typename Args::List ArgsList;
typedef typename VAL::List ValList;
typedef typename Types<ValList>::Seq ValTypes;
using Args = typename _Fun<SIG>::Args;
using Ret = typename _Fun<SIG>::Ret;
using ArgsList = typename Args::List;
using ValList = typename VAL::List;
using ValTypes = typename Types<ValList>::Seq;
enum { ARG_CNT = count<ArgsList>::value
, VAL_CNT = count<ValList> ::value
@ -580,24 +584,24 @@ namespace func{
// create list of the *remaining* arguments, after applying the ValList
typedef typename Splice<ArgsList, ValList>::Back LeftReduced;
typedef typename Splice<ArgsList, ValList, ROFFSET>::Front RightReduced;
using LeftReduced = typename Splice<ArgsList, ValList>::Back;
using RightReduced = typename Splice<ArgsList, ValList, ROFFSET>::Front;
typedef typename Types<LeftReduced>::Seq ArgsL;
typedef typename Types<RightReduced>::Seq ArgsR;
using ArgsL = typename Types<LeftReduced>::Seq;
using ArgsR = typename Types<RightReduced>::Seq;
// build a list, where each of the *remaining* arguments is replaced by a placeholder marker
typedef typename func::PlaceholderTuple<LeftReduced>::List TrailingPlaceholders;
typedef typename func::PlaceholderTuple<RightReduced>::List LeadingPlaceholders;
using TrailingPlaceholders = typename func::PlaceholderTuple<LeftReduced>::List;
using LeadingPlaceholders = typename func::PlaceholderTuple<RightReduced>::List;
// ... and splice these placeholders on top of the original argument type list,
// thus retaining the types to be closed, but setting a placeholder for each remaining argument
typedef typename Splice<ArgsList, TrailingPlaceholders, VAL_CNT>::List LeftReplaced;
typedef typename Splice<ArgsList, LeadingPlaceholders, 0 >::List RightReplaced;
using LeftReplaced = typename Splice<ArgsList, TrailingPlaceholders, VAL_CNT>::List;
using RightReplaced = typename Splice<ArgsList, LeadingPlaceholders, 0 >::List;
typedef typename Types<LeftReplaced>::Seq LeftReplacedTypes;
typedef typename Types<RightReplaced>::Seq RightReplacedTypes;
using LeftReplacedTypes = typename Types<LeftReplaced>::Seq;
using RightReplacedTypes = typename Types<RightReplaced>::Seq;
// create a "builder" helper, which accepts exactly the value tuple elements
// and puts them at the right location, while default-constructing the remaining
@ -623,8 +627,8 @@ namespace func{
public:
typedef function<typename BuildFunType<Ret,ArgsL>::Sig> LeftReducedFunc;
typedef function<typename BuildFunType<Ret,ArgsR>::Sig> RightReducedFunc;
using LeftReducedFunc = function<typename BuildFunType<Ret,ArgsL>::Sig>;
using RightReducedFunc = function<typename BuildFunType<Ret,ArgsR>::Sig>;
/** do a partial function application, closing the first arguments<br/>
@ -634,11 +638,16 @@ namespace func{
* @param arg value tuple, used to close function arguments starting from left
* @return new function object, holding copies of the values and using them at the
* closed arguments; on invocation, only the remaining arguments need to be supplied.
* @note BuildL, i.e. the TupleApplicator _must take its arguments by-value._ Any attempt
* towards »perfect-forwarding« would be potentially fragile and not worth the effort,
* since the optimiser sees the operation as a whole.
* @todo 2/2025 However, the LeftReplacedArgs _could_ then possibly moved into the bind function,
* as could the functor, once we replace the Apply-template by STDLIB features.
*/
static LeftReducedFunc
bindFront (SIG const& f, Tuple<ValTypes> const& arg)
bindFront (SIG const& f, Tuple<ValTypes> arg)
{
LeftReplacedArgs params {BuildL(arg)};
LeftReplacedArgs params {BuildL(std::move(arg))};
return func::Apply<ARG_CNT>::template bind<LeftReducedFunc> (f, params);
}
@ -651,9 +660,9 @@ namespace func{
* closed arguments; on invocation, only the remaining arguments need to be supplied.
*/
static RightReducedFunc
bindBack (SIG const& f, Tuple<ValTypes> const& arg)
bindBack (SIG const& f, Tuple<ValTypes> arg)
{
RightReplacedArgs params {BuildR(arg)};
RightReplacedArgs params {BuildR(std::move(arg))};
return func::Apply<ARG_CNT>::template bind<RightReducedFunc> (f, params);
}
};
@ -667,21 +676,23 @@ namespace func{
template<typename SIG, typename X, uint pos>
class BindToArgument
{
typedef typename _Fun<SIG>::Args Args;
typedef typename _Fun<SIG>::Ret Ret;
typedef typename Args::List ArgsList;
typedef typename Types<X>::List ValList;
using Args = typename _Fun<SIG>::Args;
using Ret = typename _Fun<SIG>::Ret;
using ArgsList = typename Args::List;
using ValList = typename Types<X>::List;
enum { ARG_CNT = count<ArgsList>::value };
typedef typename Splice<ArgsList, ValList, pos>::Front RemainingFront;
typedef typename Splice<ArgsList, ValList, pos>::Back RemainingBack;
typedef typename func::PlaceholderTuple<RemainingFront>::List PlaceholdersBefore;
typedef typename func::PlaceholderTuple<RemainingBack,pos+1>::List PlaceholdersBehind;
typedef typename Append< typename Append< PlaceholdersBefore
, ValList >::List
, PlaceholdersBehind >::List PreparedArgs;
typedef typename Append<RemainingFront, RemainingBack>::List ReducedArgs;
using RemainingFront = typename Splice<ArgsList, ValList, pos>::Front;
using RemainingBack = typename Splice<ArgsList, ValList, pos>::Back;
using PlaceholdersBefore = typename func::PlaceholderTuple<RemainingFront>::List;
using PlaceholdersBehind = typename func::PlaceholderTuple<RemainingBack,pos+1>::List;
using PreparedArgs = typename Append< typename Append< PlaceholdersBefore
, ValList >::List
, PlaceholdersBehind
>::List;
using ReducedArgs = typename Append<RemainingFront, RemainingBack>::List;
using PreparedArgTypes = typename Types<PreparedArgs>::Seq;
using RemainingArgs = typename Types<ReducedArgs>::Seq;
@ -696,12 +707,12 @@ namespace func{
public:
typedef function<ReducedSig> ReducedFunc;
using ReducedFunc = function<ReducedSig>;
static ReducedFunc
reduced (SIG& f, X val)
{
Tuple<PreparedArgTypes> params {BuildPreparedArgs{std::forward_as_tuple (val)}};
Tuple<PreparedArgTypes> params {BuildPreparedArgs{std::make_tuple (val)}};
return func::Apply<ARG_CNT>::template bind<ReducedFunc> (f, params);
}
};

View file

@ -72,25 +72,28 @@ namespace meta{
template<typename...VALS>
static auto
closeFront (VALS ...vs)
closeFront (VALS&& ...vs)
{
using ClosedTypes = TySeq<VALS...>;
return wrapBuilder (func::PApply<TupleBuilderSig, ClosedTypes>::bindFront (buildRecord, std::make_tuple(vs...)));
using ClosedTypes = TySeq<std::decay_t<VALS>...>;
auto boundArgs = std::make_tuple (std::forward<VALS> (vs)...); // Note: must be passed by-val here
return wrapBuilder (func::PApply<TupleBuilderSig, ClosedTypes>::bindFront (buildRecord, move(boundArgs)));
}
template<typename...VALS>
static auto
closeBack (VALS ...vs)
closeBack (VALS&& ...vs)
{
using ClosedTypes = TySeq<VALS...>;
return wrapBuilder (func::PApply<TupleBuilderSig, ClosedTypes>::bindBack (buildRecord, std::make_tuple(vs...)));
using ClosedTypes = TySeq<std::decay_t<VALS>...>;
auto boundArgs = std::make_tuple (std::forward<VALS> (vs)...); // Note: must be passed by-val here
return wrapBuilder (func::PApply<TupleBuilderSig, ClosedTypes>::bindBack (buildRecord, move(boundArgs)));
}
template<size_t idx, typename VAL>
static auto
close (VAL val)
close (VAL&& val)
{
return wrapBuilder (func::BindToArgument<TupleBuilderSig,VAL,idx>::reduced (buildRecord, val));
using BoundVal = std::decay_t<VAL>;
return wrapBuilder (func::BindToArgument<TupleBuilderSig,BoundVal,idx>::reduced (buildRecord, std::forward<VAL>(val)));
}
private:

View file

@ -58819,7 +58819,7 @@
</node>
</node>
</node>
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1700669580665" FOLDED="true" ID="ID_1852128670" MODIFIED="1739742209442" TEXT="Funktor-Manipulationen">
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1700669580665" FOLDED="true" ID="ID_1852128670" MODIFIED="1739826827488" TEXT="Funktor-Manipulationen">
<icon BUILTIN="hourglass"/>
<icon BUILTIN="pencil"/>
<node CREATED="1700669596447" ID="ID_1886923243" MODIFIED="1700669617888" TEXT="einige Werkzeuge hatte ich vor sehr langer Zeit schon gebaut....">
@ -58971,6 +58971,68 @@
<node CREATED="1739578282176" ID="ID_1393053020" MODIFIED="1739578297573" TEXT="Hauptfokus beim Umbau sollte das perfect-Forwarding sein"/>
<node CREATED="1739578299345" ID="ID_1545493795" MODIFIED="1739578332823" TEXT="dagegen die Positions-Steuerung sollte weiterhin per Typ-Sequenz-Manipulation erfolgen"/>
</node>
<node BACKGROUND_COLOR="#e9e0c4" COLOR="#690f14" CREATED="1739826188337" ID="ID_783082623" MODIFIED="1739827057895" TEXT="m&#xf6;gliche Verbesserungen">
<linktarget COLOR="#fef2de" DESTINATION="ID_783082623" ENDARROW="Default" ENDINCLINATION="-2327;222;" ID="Arrow_ID_897208269" SOURCE="ID_1631025117" STARTARROW="Default" STARTINCLINATION="1408;-47;"/>
<icon BUILTIN="idea"/>
<node CREATED="1739826202691" ID="ID_1553267195" MODIFIED="1739826931700">
<richcontent TYPE="NODE"><html>
<head/>
<body>
<p>
Achtung: perfect-forwarding in das Tuple-Remapping <i>hinein </i>w&#228;re <font color="#d10606">gef&#228;hrlich</font>
</p>
</body>
</html>
</richcontent>
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
Habe das <b>im Detail analysiert</b>.
</p>
<ul>
<li>
vor dem Binding wird ein remappetes Tupel konstruiert, bei dem u.U einzelne Werte durch Placeholder ersetzt wurden (und andere Werte an andere Positionen gingen)
</li>
<li>
dies baut auf TupleConstructor / ElmMapper auf. TupleConstructor nimmt das Tupel <b>per-Value</b>, ElmMapper reicht darauf eine Referenz
</li>
<li>
das ist <b>essentiell</b>, weil dadurch ElmMapper v&#246;llig frei ist und beliebige Mappings, auch mehrfach-Mappings realisieren kann
</li>
<li>
w&#252;rde in dieses Vorstufen-Tupel eine RValue-Referenz gelangen, und w&#252;rde man im ElmMapper eine RValue-Referenz durchreichen, dann w&#252;rde <b>jeder Wert sofort konsumiert</b>.
</li>
</ul>
</body>
</html></richcontent>
</node>
<node CREATED="1739826248153" ID="ID_213763590" MODIFIED="1739826386851" TEXT="das remapped Tuple (mit Placeholdern) mu&#xdf; als Value f&#xfc;r den Binder zur Verf&#xfc;gung stehen">
<icon BUILTIN="messagebox_warning"/>
</node>
<node CREATED="1739826282753" ID="ID_1089732209" MODIFIED="1739827038493" TEXT="aber man k&#xf6;nnte dieses Zwischen-Tupel dann per RValue-Ref zum Binder bringen">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
Denn: wenn man std::get das Quell-Tupel als RValue-Ref gibt, dann wird diese Funktion auch die Inhalte als RValue-Ref exponieren; im Zusammenspiel mit std::forward_as_tuple w&#228;re so &#187;perfect-forwarding&#171; m&#246;glich, und f&#252;r diesen <b>zweiten Schritt</b>&#160;auch sinnvoll, denn das remapped-Zwischen-Tupel bauen wir nur einmal auf, um es dann elementweise an den Binder weiterzugeben. Der Binder wiederum nimmt ausschlie&#223;lich die zu bindenden Elemente (nicht die Placeholder) per RValue-Referenz und schiebt sie in ein Tuple in inline-Storage im Binder selber, wo sie dauerhaft liegen m&#252;ssen, damit der Binder-Funktor beliebig oft aufgerufen werden kann
</p>
</body>
</html></richcontent>
<icon BUILTIN="idea"/>
</node>
<node CREATED="1739826348755" ID="ID_1933382686" MODIFIED="1739826806174" TEXT="und man k&#xf6;nnte den Funktor stets per move in bind / apply schieben">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
std::bind und std::apply schiebein alle Argumente per perfect-forwarding durch. Damit k&#246;nnte man auch move-only-Funktoren verarbeiten, <b>was die bestehende Impl nicht kann</b>.
</p>
</body>
</html></richcontent>
<icon BUILTIN="idea"/>
</node>
</node>
</node>
<node COLOR="#338800" CREATED="1700672430249" HGAP="136" ID="ID_975422639" MODIFIED="1700686519692" TEXT="schrittweise vorgehen...." VSHIFT="-7">
<icon BUILTIN="pencil"/>
@ -105997,8 +106059,28 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
</node>
<node BACKGROUND_COLOR="#fafe99" COLOR="#fa002a" CREATED="1739669530247" ID="ID_1438322146" MODIFIED="1739669555839" TEXT="fehlt noch">
<icon BUILTIN="bell"/>
<node CREATED="1739669562919" ID="ID_330946433" MODIFIED="1739669583079" TEXT="Ausschlu&#xdf; von Aufrufen ohne Parameter"/>
<node CREATED="1739669585924" ID="ID_211735922" MODIFIED="1739669597954" TEXT="Spezialbehandlung wenn die Closure nur noch ein Argument nimmt"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1739669562919" ID="ID_330946433" MODIFIED="1739831185269" TEXT="Ausschlu&#xdf; von Aufrufen ohne Parameter">
<icon BUILTIN="yes"/>
</node>
<node COLOR="#5b280f" CREATED="1739669585924" ID="ID_211735922" MODIFIED="1739831065154" TEXT="Spezialbehandlung wenn die Closure nur noch ein Argument nimmt">
<icon BUILTIN="button_cancel"/>
<node COLOR="#5b280f" CREATED="1739831066833" ID="ID_1808339038" MODIFIED="1739831075272" TEXT="nein! das mache ich nicht">
<icon BUILTIN="button_cancel"/>
</node>
<node CREATED="1739831077071" ID="ID_53230918" MODIFIED="1739831091447" TEXT="es ist jetzt als generisches Tool extrahiert"/>
<node COLOR="#435e98" CREATED="1739831092845" ID="ID_791772098" MODIFIED="1739831169723" TEXT="und die implizite Konstruktor-conversion erledigt den Rest">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
d.h. man <i>kann</i>&#160;in der Praxis direkt mit einem Wert aufrufen, dann wird der eben implizit in ein 1-Tupel konvertiert
</p>
</body>
</html>
</richcontent>
<icon BUILTIN="idea"/>
</node>
</node>
</node>
</node>
</node>
@ -106024,16 +106106,15 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
<node COLOR="#435e98" CREATED="1739743706096" ID="ID_1310970069" MODIFIED="1739743735573" TEXT="das ist eigentlich generisch &#x27f9; umziehen in lib::meta::TupleClosureBuilder">
<icon BUILTIN="yes"/>
</node>
<node COLOR="#338800" CREATED="1739752776321" ID="ID_376565948" MODIFIED="1739752785816" TEXT="TupleClosure_test zur Dokumentation">
<node COLOR="#338800" CREATED="1739752776321" ID="ID_376565948" MODIFIED="1739831050309" TEXT="TupleClosure_test zur Dokumentation">
<linktarget COLOR="#5fcdb6" DESTINATION="ID_376565948" ENDARROW="Default" ENDINCLINATION="-22;74;" ID="Arrow_ID_1770813109" SOURCE="ID_656057721" STARTARROW="None" STARTINCLINATION="-338;20;"/>
<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">
<node COLOR="#435e98" CREATED="1739822430227" ID="ID_1498733913" MODIFIED="1739827102416">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
wie steht's mit <i>perfect forwarding</i>?
@ -106045,9 +106126,7 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
<icon BUILTIN="help"/>
<node CREATED="1739822460457" ID="ID_813032583" MODIFIED="1739822493962">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
ohnehin limitiert: wir konstruieren stets Werte <i>in den Binder</i>
@ -106062,22 +106141,70 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
<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">
<node CREATED="1739825076184" ID="ID_1261900535" MODIFIED="1739825789944" TEXT="einzelne Elemente werden per std::get&lt;i&gt; zum Ziel-Tupel gebracht">
<richcontent TYPE="NOTE"><html>
<head>
</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
...das bedeutet, der ElmMapper greift auf Inhalte zu, so wie sie im ctor-Argument des TupleConstructors liegen. Aber, da das Tupel selber per L-Value-Referenz eingebunden ist, wird auch std::get nur eine LValue-Referenz herausgeben (selbst wenn im Tupel selber eine RValue-Referenz liegen w&#252;rde...). Das w&#228;re nur anders, wenn man <i>das ganze Tupel</i>&#160;als RValue-Referenz an std::get g&#228;be...
</p>
</body>
</html>
</richcontent>
<icon BUILTIN="info"/>
</node>
<node CREATED="1739822742383" ID="ID_1631025117" MODIFIED="1739827057895">
<richcontent TYPE="NODE"><html>
<head/>
<body>
<p>
das ist hier sogar ein <b>essentielles Feature</b>
</p>
</body>
</html></richcontent>
<richcontent TYPE="NOTE"><html>
<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. Denn wenn man das eingebundene Tupel als RValue an std::get geben w&#252;rde, dann w&#252;rde dieses eine RValue-Referenz <b>von Value-Inhalten ziehen</b>, und diese damit beim ersten Zugriff konsumieren.
</p>
</body>
</html></richcontent>
<arrowlink COLOR="#fef2de" DESTINATION="ID_783082623" ENDARROW="Default" ENDINCLINATION="-2327;222;" ID="Arrow_ID_897208269" STARTARROW="Default" STARTINCLINATION="1408;-47;"/>
<icon BUILTIN="stop-sign"/>
</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 CREATED="1739822955768" ID="ID_82653424" MODIFIED="1739830817696" TEXT="wir k&#xf6;nnen also nur Werte bis zum Tuple-Constructor bringen">
<icon BUILTIN="stop-sign"/>
</node>
<node COLOR="#338800" CREATED="1739822976729" ID="ID_539739884" MODIFIED="1739830821068" TEXT="aber: BindToArgument k&#xf6;nnte einen einfachen Wert nehmen">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1739830828065" ID="ID_368922829" MODIFIED="1739830849652" TEXT="und der Eingan in die bind*-Funktionen kann die Values per forwarding-Ref nehmen">
<icon BUILTIN="button_ok"/>
<node CREATED="1739830851345" ID="ID_1868790946" MODIFIED="1739830881500" TEXT="Achtung: dann aber decay_t&lt;VAL&gt; anwenden">
<icon BUILTIN="messagebox_warning"/>
</node>
<node CREATED="1739830885089" ID="ID_32350662" MODIFIED="1739830909941" TEXT="sonst wird der Binder falsch konfiguriert">
<icon BUILTIN="broken-line"/>
</node>
<node COLOR="#435e98" CREATED="1739830914733" ID="ID_1413902750" MODIFIED="1739830936444" TEXT="(ist korrekt: wir wollen den decay_t speichern)">
<font NAME="SansSerif" SIZE="10"/>
</node>
</node>
</node>
<node COLOR="#338800" CREATED="1739830949537" ID="ID_411493705" MODIFIED="1739830992795" TEXT="die weitere Analyse an mehreren Stellen notiert">
<icon BUILTIN="button_ok"/>
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1739830965038" ID="ID_1001623953" MODIFIED="1739830988214" TEXT="und auf sp&#xe4;ter aufgeschoben">
<icon BUILTIN="hourglass"/>
</node>
<node CREATED="1739830973230" ID="ID_1026539253" MODIFIED="1739830984864" TEXT="wenn die Apply-Tempaltes abgel&#xf6;st werden">
<icon BUILTIN="info"/>
</node>
</node>
<node COLOR="#338800" CREATED="1739830994738" ID="ID_656057721" MODIFIED="1739831050309" TEXT="Unit-Test f&#xfc;r Closure von Einzel-Argumenten">
<arrowlink COLOR="#5fcdb6" DESTINATION="ID_376565948" ENDARROW="Default" ENDINCLINATION="-22;74;" ID="Arrow_ID_1770813109" STARTARROW="None" STARTINCLINATION="-338;20;"/>
<icon BUILTIN="button_ok"/>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1739756728914" ID="ID_1671606956" MODIFIED="1739813490672" TEXT="Auf std::array anwendbar machen">