Invocation: integration test to use the Param Agent Node Builder

incidentally, this is also the first test case ever to involve linked nodes,
so it revealed several bugs in the related code, which was not yet tested.

This is a ''move-builder'' and thus represents a tricky and sometimes dangerous setup,
while allowing to switch the type context in the middle of the build process.
It is essential to return a RValue-Reference from all builder calls which
stay on the same builder context.

After fixing those minor (and potentially dangerous) aspects regarding move-references,
the code built yesterday worked as expected!
This commit is contained in:
Fischlurch 2025-01-04 19:28:58 +01:00
parent 79f365df67
commit 16a6a0d630
4 changed files with 240 additions and 96 deletions

View file

@ -54,7 +54,6 @@
** Level-2 builder operations bottom-up to generate and wire up the corresponding Render Nodes.
**
** ## Using custom allocators
**
** Since the low-level-Model is a massive data structure comprising thousands of nodes, each with
** specialised parametrisation for some media handling library, and a lot of cross-linking pointers,
** it is important to care for efficient usage of memory with good locality. Furthermore, the higher
@ -68,6 +67,35 @@
** @remark syntactically, the custom allocator specification is given after opening a top-level
** builder, by means of the builder function `.withAllocator<ALO> (args...)`
**
**
** # Building Render Nodes
**
** At Level-2, actual render nodes are generated. The NodeBuilder creates a suitably configured
** \ref Connectivity object, which can be dropped directly into a ProcNode. Managing the storage
** of those Render Nodes themselves is beyond the scope of the builder; so the user of the builder
** is responsible for the lifecycle of generated ProcNode objects.
**
** ## Flavours of the processing function
** The binding to the actual data processing operations (usually supplied by an external library)
** is established by a **processing-functor** passed to configure the [Port builder](\PortBuilderRoot::invoke()).
** The supported signatures of this functor are quite flexible to allow for various flavours of invocation.
** Data types of parameters and buffers are picked up automatically (at compile time), based on the
** signature of the actual function supplied. The accepted variations are described in detail
** [here](\ref feed-manifold.hpp). Basically, a function can take parameters, input- and output-buffers,
** yet only the output-buffers are mandatory. Several elements of one kind can be passed as tuple.
**
** ## Handling of Invocation Parameters
** Typically, a processing operation can be configured in various ways, by passing additional
** setup- and invocation parameters. This entails both technical aspects (like picking some specific
** data format), organisational concerns (like addressing a specific frame-number) and elements of
** artistic control, like choosing the settings of a media processing effect. Parameters will thus
** be collected from various sources, which leads to an additional binding step, where all these
** sources are retrieved and the actual parameter value or value tuple is produced. This specific
** _parameter binding_ is represented as a **parameter-functor**. Whenever the processing-function
** accepts a parameter argument, optionally a such parameter-functor can be installed; this functor
** is supplied with the \ref TurnoutSystem of the actual invocation, which acts as front-end to
** access contextual parameters.
**
** @todo WIP-WIP-WIP 10/2024 Node-Invocation is reworked from ground up -- some parts can not be
** spelled out completely yet, since we have to build this tightly interlocked system of
** code moving bottom up, and then filling in further details later working top-down.
@ -105,6 +133,7 @@ namespace engine {
using util::_Fmt;
using std::forward;
using std::move;
using std::ref;
namespace { // default policy configuration to use heap allocator
@ -168,10 +197,10 @@ namespace engine {
friend class NodeBuilder;
NodeBuilder
NodeBuilder&&
addLead (ProcNode const& lead)
{
leads_.append (lead);
leads_.append (ref(lead));
return move(*this);
}
@ -284,7 +313,7 @@ namespace engine {
public:
template<class ILA, typename...ARGS>
PortBuilder
PortBuilder&&
createBuffers (ARGS&& ...args)
{
UNIMPLEMENTED ("define builder for all buffers to use");
@ -293,7 +322,7 @@ namespace engine {
/** define the output slot to use as result
* @remark default is to use the first one */
PortBuilder
PortBuilder&&
asResultSlot (uint r)
{
weavingBuilder_.selectResultSlot(r);
@ -306,21 +335,21 @@ namespace engine {
* when a top-level node exposes N different flavours, its predecessors will very
* likely also be configured to produce the pre-product for these flavours.
*/
PortBuilder
PortBuilder&&
connectLead (uint idx)
{
return connectLeadPort (idx, this->defaultPort_);
}
/** connect the next input slot to either existing or new lead-node" */
PortBuilder
PortBuilder&&
conectLead (ProcNode& leadNode)
{
return connectLeadPort (leadNode, this->defaultPort_);
}
/** connect next input to lead-node, using a specific port-number */
PortBuilder
PortBuilder&&
connectLeadPort (uint idx, uint port)
{
if (idx >= _Par::leads_.size())
@ -333,7 +362,7 @@ namespace engine {
}
/** connect next input to existing or new lead-node, with given port-number */
PortBuilder
PortBuilder&&
connectLeadPort (ProcNode& leadNode, uint port)
{
uint knownEntry{0};
@ -350,7 +379,7 @@ namespace engine {
}
/** use given port-index as default for all following connections */
PortBuilder
PortBuilder&&
useLeadPort (uint defaultPort)
{
this->defaultPort_ = defaultPort;
@ -502,21 +531,21 @@ namespace engine {
* when a top-level node exposes N different flavours, its predecessors will very
* likely also be configured to produce the pre-product for these flavours.
*/
ParamAgentBuilder
ParamAgentBuilder&&
delegateLead (uint idx)
{
return delegateLeadPort (idx, defaultPortNr_);
}
/** use the given node as delegate, but also possibly register it as lead node */
ParamAgentBuilder
ParamAgentBuilder&&
delegateLead (ProcNode& leadNode)
{
return delegateLeadPort (leadNode, defaultPortNr_);
}
/** use a lead node and specific port as delegate to invoke with extended parameters */
ParamAgentBuilder
ParamAgentBuilder&&
delegateLeadPort (uint idx, uint port)
{
if (idx >= _Par::leads_.size())
@ -524,13 +553,14 @@ namespace engine {
% idx % _Par::leads_.size()
,LERR_(INDEX_BOUNDS)
};
delegatePort_ = & _Par::leads_[idx].getPort (port);
ProcNode& leadNode = _Par::leads_[idx];
delegatePort_ = & leadNode.getPort (port);
return move(*this);
}
/** use the specific port on the given node as delegate,
* while possibly also registering it as lead node. */
ParamAgentBuilder
ParamAgentBuilder&&
delegateLeadPort (ProcNode& leadNode, uint port)
{
uint knownEntry{0};
@ -556,7 +586,7 @@ namespace engine {
* @remark the purpose is to enable coordinated adjustments on all parameters together,
* immediately before delegating to the nested node evaluation with these parameters.
*/
ParamAgentBuilder
ParamAgentBuilder&&
installPostProcessor(PostProcessor pp)
{
postProcessor_ = move(pp);
@ -600,7 +630,6 @@ namespace engine {
} // chain back up to Node-Builder with extended patternData
private:
template<typename FUN>
ParamAgentBuilder(_Par&& base, BlockBuilder&& builder)
: _Par{move(base)}
, blockBuilder_{move(builder)}
@ -645,10 +674,11 @@ namespace engine {
template<class POL, class DAT>
template<class SPEC>
auto
PortBuilderRoot<POL,DAT>::computeParam(SPEC&& spec)
PortBuilderRoot<POL,DAT>::computeParam(SPEC&& ref)
{
using ParamBuildSpec = std::decay_t<SPEC>;
return ParamAgentBuilder<POL,DAT,ParamBuildSpec>{spec.makeBlockBuilder()};
ParamBuildSpec spec {forward<SPEC>(ref)}; // consumes the spec
return ParamAgentBuilder<POL,DAT,ParamBuildSpec>{move(*this), spec.makeBlockBuilder()};
}

View file

@ -15,16 +15,16 @@
/** @file param-weaving-pattern.hpp
** Construction kit to establish a set of parameters pre-computed prior to invocation
** of nested nodes. This arrangement is also known as »Parameter Agent Node« (while actually
** it is a Weaving Patter residing within some Node's Port). The use-case is to provide a set
** it is a Weaving Pattern residing within some Node's Port). The use-case is to provide a set
** of additional parameter values, beyond what can be derived directly by a parameter-functor
** based on the _absolute-nominal-Time_ of the invocation. The necessity for such a setup may
** arise when additional context or external state must be combined with the nominal time into
** a tuple of data values, which shall then be consumed by several follow-up evaluations further
** down into a recursive invocation tree _for one single render job._ The solution provided by
** the Parameter Agent Node relies on placing those additional data values into a tuple stored
** directly in the render invocation stack frame, prior to descending into further recursive
** Node evaluations. Notably, parameter-functors within the scope of this evaluation tree can
** then access these additional parameters through the TurnoutSystem of the overall invocation.
** the Parameter Agent Node relies on placing those additional data values into a tuple, which
** is then stored directly in the render invocation stack frame, prior to descending into further
** recursive Node evaluations. Notably, parameter-functors within the scope of this evaluation tree
** can then access these additional parameters through the TurnoutSystem of the overall invocation.
**
** @see node-builder.hpp
** @see weaving-pattern-builder.hpp
@ -42,7 +42,6 @@
#include "steam/common.hpp"
#include "steam/engine/turnout.hpp"
#include "steam/engine/turnout-system.hpp"
#include "steam/engine/feed-manifold.hpp" ////////////TODO wegdamit
#include "lib/uninitialised-storage.hpp"
#include "lib/meta/variadic-helper.hpp"
#include "lib/meta/tuple-helper.hpp"
@ -65,16 +64,17 @@ namespace engine {
using std::function;
using std::make_tuple;
using std::tuple;
using lib::Several;////TODO RLY?
using lib::meta::Tuple;
using lib::meta::ElmTypes;
template<class ANK, typename...FUNZ>
template<class ANCH, typename...FUNZ>
struct ParamBuildSpec
{
using Functors = tuple<FUNZ...>;
using ResTypes = typename lib::meta::ElmTypes<Functors>::template Apply<lib::meta::_FunRet>;
using ParamTup = lib::meta::Tuple<ResTypes>;
using ResTypes = typename ElmTypes<Functors>::template Apply<lib::meta::_FunRet>;
using ParamTup = Tuple<ResTypes>;
Functors functors_;
@ -82,12 +82,17 @@ namespace engine {
: functors_{move (funz)}
{ }
/** can be copied if all functors are copyable... */
ParamBuildSpec clone() { return *this; }
template<typename FUN>
auto
addSlot (FUN&& paramFun)
{
return ParamBuildSpec<ANK,FUNZ...,FUN>{std::tuple_cat (move(functors_)
,make_tuple (forward<FUN>(paramFun)))};
using FunN = std::decay_t<FUN>;
return ParamBuildSpec<ANCH,FUNZ...,FunN>{std::tuple_cat (move(functors_)
,make_tuple (forward<FUN>(paramFun)))};
}
template<typename PAR>
@ -112,14 +117,13 @@ namespace engine {
* @remark HeteroData defines a nested struct `Chain`, and with the help of `RebindVariadic`,
* the type sequence from the ParamTup can be used to instantiate this Chain context.
*/
using ChainCons = typename lib::meta::RebindVariadic<ANK::template Chain, ParamTup>::Type;
using ChainCons = typename lib::meta::RebindVariadic<ANCH::template Chain, ParamTup>::Type;
/** a (static) getter functor able to work on the full extended HeteroData-Chain
* @remark the front-end of this chain resides in TurnoutSystem */
template<size_t slot>
struct Accessor
: util::MoveOnly
{
static auto&
getParamVal (TurnoutSystem& turnoutSys)
@ -233,7 +237,7 @@ namespace engine {
/** Preparation: create a Feed data frame to use as local scope */
Feed
mount (TurnoutSystem& turnoutSys)
mount (TurnoutSystem&)
{
return Feed{};
}
@ -265,6 +269,7 @@ namespace engine {
weft (Feed& feed, TurnoutSystem& turnoutSys)
{
feed.outBuff = delegatePort_.weave (turnoutSys, feed.outBuff);
ENSURE (feed.outBuff);
}
/** clean-up: detach the parameter-data-block.
@ -274,7 +279,7 @@ namespace engine {
fix (Feed& feed, TurnoutSystem& turnoutSys)
{
turnoutSys.detachChainBlock(feed.block());
return feed.outBuff;
return *feed.outBuff;
}
};

View file

@ -39,6 +39,8 @@ using lib::time::Time;
using lib::time::FSecs;
using lib::time::FrameNr;
using lib::test::showType;
using std::make_tuple;
using std::get;
namespace steam {
@ -109,6 +111,7 @@ namespace test {
}
/** @test create extended parameter data for use in recursive Node invocation.
* - demonstrate the mechanism of param-functor invocation,
* and how a Param-Spec is built to create and hold those functors
@ -122,71 +125,120 @@ namespace test {
void
feedParamNode()
{
steam::asset::meta::TimeGrid::build("grid_sec", 1);
// Assuming that somewhere in the system a 1-seconds time grid was predefined...
steam::asset::meta::TimeGrid::build ("grid_sec", 1);
// Parameter-functor based on time-quantisation into a 1-seconds-grid
auto fun1 = [](TurnoutSystem& turSys)
{
return FrameNr::quant (turSys.getNomTime(), "grid_sec");
};
//_______________________________________________
// Demo-1: demonstrate the access mechanism directly;
// create and link an extended parameter block.
// The Param-Spec is used to coordinate type-safe access
// This test will create an extension data block with two parameters,
// one of these is generated from time-quantisation into a 1-seconds-grid
auto createParmFun = [](TurnoutSystem& turnoutSys) -> long
{
return FrameNr::quant (turnoutSys.getNomTime(), "grid_sec");
};
// The »Param-Spec« is used to coordinate type-safe access
// and also is used as a blueprint for building a Param(Agent)Node
// Note the builder syntax to add several parameter »slots«...
auto spec = buildParamSpec()
.addValSlot (LIFE_AND_UNIVERSE_4EVER)
.addSlot (move (fun1))
.addSlot (createParmFun)
;
// The implied type of the parameter-tuple to generate
// Implied type of the parameter-tuple to generate
using ParamTup = decltype(spec)::ParamTup;
CHECK (showType<ParamTup>() == "tuple<uint, long>"_expect);
// can now store accessor-functors for later use....
auto acc0 = spec.makeAccessor<0>();
auto acc0 = spec.makeAccessor<0>(); // can now store accessor-functors for later use....
auto acc1 = spec.makeAccessor<1>();
// drive test with a random »nominal Time« <10s with ms granularity
Time nomTime{rani(10'000),0};
TurnoutSystem turnoutSys{nomTime};
// can now immediately invoke the embedded parameter-functors
auto v0 = spec.invokeParamFun<0> (turnoutSys);
// Prepare for invocation....
Time nomTime{rani(10'000),0}; // drive test with a random »nominal Time« <10s with ms granularity
TurnoutSystem turnoutSys{nomTime}; // build minimal TurnoutSystem for invocation, just with this time parameter
auto v0 = spec.invokeParamFun<0> (turnoutSys); // can now immediately invoke the embedded parameter-functors
auto v1 = spec.invokeParamFun<1> (turnoutSys);
CHECK (v0 == LIFE_AND_UNIVERSE_4EVER); // ◁————————— the first paramFun yields the configured fixed value
CHECK (v1 == FrameNr::quant (nomTime, "grid_sec")); // ◁————————— the second paramFun accesses the time in TurnoutSystem
CHECK (v0 == LIFE_AND_UNIVERSE_4EVER); // ◁————————— the first paramFun yields the configured fixed value
CHECK (v1 == FrameNr::quant (nomTime, "grid_sec")); // ◁————————— the second paramFun accesses the time via TurnoutSystem
// after all setup of further accessor functors is done
// finally transform the ParamSpec into a storage-block-builder:
auto blockBuilder = spec.makeBlockBuilder();
// finally transform the ParamSpec into a storage-block-builder
auto blockBuilder = spec.clone().makeBlockBuilder(); // (use clone() since we're re-using the same spec in Demo-2 below)
{ // Now build an actual storage block in local scope,
// thereby invoking the embedded parameter-functors...
auto paramBlock = blockBuilder.buildParamDataBlock (turnoutSys);
// Values are now materialised into paramBlock
CHECK (v0 == paramBlock.get<0>());
CHECK (v0 == paramBlock.get<0>()); // Values are now materialised into paramBlock
CHECK (v1 == paramBlock.get<1>());
// link this extension block into the parameter-chain in TurnoutSystem
turnoutSys.attachChainBlock(paramBlock);
turnoutSys.attachChainBlock(paramBlock); // link this extension block into the parameter-chain in TurnoutSystem;
CHECK (v0 == acc0.getParamVal (turnoutSys)); // Can now access the parameter values through the TurnoutSystem as front-End
CHECK (v1 == acc1.getParamVal (turnoutSys)); // ...using the pre-configured accessor-functors stored above
// can now access the parameter values through the TurnoutSystem as front-End
// using the pre-configured accessor-functors stored above
CHECK (v0 == acc0.getParamVal (turnoutSys));
CHECK (v1 == acc1.getParamVal (turnoutSys));
// should detach extension block before leaving scope
turnoutSys.detachChainBlock(paramBlock);
}
using Spec = decltype(spec);
using WaPa = ParamWeavingPattern<Spec>;
using Feed = WaPa::Feed;
turnoutSys.detachChainBlock(paramBlock); // should detach extension block before leaving scope
}// extension block is gone...
Feed feed;
feed.emplaceParamDataBlock (blockBuilder, turnoutSys);
SHOW_EXPR(feed.buffer[0].get<0>())
SHOW_EXPR(feed.buffer[0].get<1>())
TODO ("implement a simple Builder for ParamAgent-Node");
TODO ("then use both together to demonstrate a param data feed here");
{ // Demonstrate the same access mechanism
// but integrated into a Weaving-Pattern
using Spec = decltype(spec);
using WeavingPattern = ParamWeavingPattern<Spec>;
using Feed = WeavingPattern::Feed;
Feed feed;
feed.emplaceParamDataBlock (blockBuilder, turnoutSys);
// note that the param-data-block is embedded into the feed,
// so that it can be easily placed into the current stack frame
CHECK (v0 == feed.block().get<0>());
CHECK (v1 == feed.block().get<1>());
}
//_________________________________________________
// Demo-2: perform exactly the same access scheme,
// but now embedded into a Render Node graph.
using Param = tuple<int,int>;
// The processing function uses two parameter values
auto processFun = [](Param par, long* buff)
{
*buff = get<0>(par) + get<1>(par);
};
// These parameter values are picked up from the extended TurnoutSystem,
// relying on the accessor objects, which were created from the ParamSpec
auto accessParam = [acc0,acc1]
(TurnoutSystem& turnoutSys) -> Param
{
return make_tuple (acc0.getParamVal (turnoutSys)
,acc1.getParamVal (turnoutSys));
};
ProcNode delegate{prepareNode("Delegate")
.preparePort()
.invoke("proc()", processFun)
.attachParamFun (accessParam)
.completePort()
.build()};
ProcNode paramAgent{prepareNode("Param")
.preparePort()
.computeParam (move(spec))
.delegateLead (delegate) // ◁————————— linked to the Delegate-Node
.completePort()
.build()};
// Prepare result buffer for invocation
BufferProvider& provider = DiagnosticBufferProvider::build();
BuffHandle buff = provider.lockBufferFor<long> (-55);
CHECK (-55 == buff.accessAs<long>());
// Invoke Port#0 on the top-level Node (≙ the ParamAgent)
buff = paramAgent.getPort(0).weave(turnoutSys, buff); // ◁————————— generate Param-Values, link into TurnoutSystem, invoke Delegate
CHECK (v0+v1 == buff.accessAs<long>());
buff.release();
}
};

View file

@ -98065,16 +98065,44 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
<node CREATED="1735417162872" ID="ID_1823137654" MODIFIED="1735417286318" TEXT="auch hier: vor allem die Strukturen &#x201e;zu Fu&#xdf;&#x201c; aufbauen....">
<icon BUILTIN="yes"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1735417346027" ID="ID_1555206108" MODIFIED="1735417364905" TEXT="zun&#xe4;chst eine ParamBuildSpec anlegen">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1735417346027" ID="ID_1555206108" MODIFIED="1736003492310" TEXT="zun&#xe4;chst eine ParamBuildSpec anlegen">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1735434511762" ID="ID_303778551" MODIFIED="1735434520154" TEXT="einfache Automations-Funktion">
<node CREATED="1735434522544" ID="ID_686486120" LINK="#ID_1295659864" MODIFIED="1735434554616" TEXT="brauche Zeit-Quantisierung"/>
<node CREATED="1735434579728" ID="ID_1335340741" LINK="#ID_276802280" MODIFIED="1735434603176" TEXT="brauche Accessor in TurnoutSystem"/>
<node COLOR="#338800" CREATED="1735434511762" ID="ID_303778551" MODIFIED="1736003501094" TEXT="einfache Automations-Funktion">
<icon BUILTIN="button_ok"/>
<node COLOR="#435e98" CREATED="1735434522544" ID="ID_686486120" LINK="#ID_1295659864" MODIFIED="1736003498255" TEXT="brauche Zeit-Quantisierung"/>
<node COLOR="#435e98" CREATED="1735434579728" ID="ID_1335340741" LINK="#ID_276802280" MODIFIED="1736003498253" TEXT="brauche Accessor in TurnoutSystem"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1735962572153" ID="ID_600039329" MODIFIED="1735962606991" TEXT="dann exakt das gleiche Setup per ParamAgentBuilder konstruieren">
<node COLOR="#338800" CREATED="1735962572153" ID="ID_600039329" MODIFIED="1736015429332">
<richcontent TYPE="NODE"><html>
<head/>
<body>
<p>
dann exakt das gleiche Setup
</p>
<p>
per ParamAgentBuilder konstruieren
</p>
</body>
</html>
</richcontent>
<linktarget COLOR="#a9b4c1" DESTINATION="ID_600039329" ENDARROW="Default" ENDINCLINATION="-387;16;" ID="Arrow_ID_1024063898" SOURCE="ID_373701114" STARTARROW="None" STARTINCLINATION="-156;-13;"/>
<icon BUILTIN="flag-yellow"/>
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1736003503677" ID="ID_1356439395" MODIFIED="1736015439809" TEXT="brauche nun eine Delegate-Node">
<icon BUILTIN="button_ok"/>
<node COLOR="#435e98" CREATED="1736003536184" ID="ID_406668355" MODIFIED="1736015437092" TEXT="der Builder verlangt Node, nicht Port"/>
<node COLOR="#435e98" CREATED="1736003546607" ID="ID_1331618974" MODIFIED="1736015437094" TEXT="&#x27f9; mu&#xdf; auch das Delegate per Builder bauen"/>
<node COLOR="#435e98" CREATED="1736003572220" ID="ID_799485617" MODIFIED="1736015437093" TEXT="sollte au&#xdf;erdem die Verwendung der Accessoren demonstrieren"/>
<node COLOR="#435e98" CREATED="1736003593281" ID="ID_1331411732" MODIFIED="1736015437092" TEXT="&#x27f9; brauche ein Parameter-Tupel"/>
</node>
<node COLOR="#338800" CREATED="1736015441305" ID="ID_1760804530" MODIFIED="1736015454335" TEXT="nebenbei: das sind die ersten vernk&#xfc;pften Nodes">
<icon BUILTIN="button_ok"/>
<node CREATED="1736015456375" ID="ID_377752415" MODIFIED="1736015464737" TEXT="(diverse kleine Fehler in diesem Code)"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1736015467161" ID="ID_1840237951" MODIFIED="1736015498589" TEXT="Vorsicht Falle: es ist ein move-Builder">
<arrowlink COLOR="#853a40" DESTINATION="ID_1628252165" ENDARROW="Default" ENDINCLINATION="199;0;" ID="Arrow_ID_133365781" STARTARROW="None" STARTINCLINATION="255;18;"/>
<icon BUILTIN="messagebox_warning"/>
</node>
</node>
</node>
</node>
</node>
@ -98083,7 +98111,7 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
<linktarget COLOR="#482c86" DESTINATION="ID_329885446" ENDARROW="Default" ENDINCLINATION="-553;-36;" ID="Arrow_ID_1024921689" SOURCE="ID_921902157" STARTARROW="None" STARTINCLINATION="598;1114;"/>
<linktarget COLOR="#5747d2" DESTINATION="ID_329885446" ENDARROW="Default" ENDINCLINATION="-844;-25;" ID="Arrow_ID_1824926310" SOURCE="ID_1257939291" STARTARROW="None" STARTINCLINATION="-633;42;"/>
<icon BUILTIN="pencil"/>
<node CREATED="1735416968791" HGAP="28" ID="ID_1420751527" MODIFIED="1735417275282" STYLE="bubble" VSHIFT="20">
<node BACKGROUND_COLOR="#bcdcd5" CREATED="1735416968791" HGAP="28" ID="ID_1420751527" MODIFIED="1736015728624" STYLE="bubble" VSHIFT="20">
<richcontent TYPE="NODE"><html>
<head/>
<body>
@ -98111,8 +98139,7 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
...die dann sp&#228;ter den eigentlichen Turnout produziert und in den DataBuilder f&#252;r die Node-Ports &#8222;abwirft&#8220;
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1735956206934" ID="ID_994628798" MODIFIED="1735956218424" TEXT="und sliced sich selbst am Ende weg"/>
</node>
@ -98157,9 +98184,9 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
<font NAME="SansSerif" SIZE="13"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1735962403144" ID="ID_141428542" MODIFIED="1735962413554" TEXT="Zusammenspiel mit dem ParamWeavingPattern">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1735962417830" ID="ID_1920831768" MODIFIED="1735962446968">
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1735962403144" ID="ID_141428542" MODIFIED="1736015707962" TEXT="Zusammenspiel mit dem ParamWeavingPattern">
<icon BUILTIN="pencil"/>
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1735962417830" ID="ID_1920831768" MODIFIED="1736015318769">
<richcontent TYPE="NODE"><html>
<head/>
<body>
@ -98167,17 +98194,47 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
rein nach Definition <i>sollte es bereits funktionieren</i>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="idea"/>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1735962448890" ID="ID_72580990" MODIFIED="1735962486906" TEXT="aber das kann ich nicht recht glauben">
<node BACKGROUND_COLOR="#c8b9b6" COLOR="#4b1682" CREATED="1735962448890" ID="ID_72580990" MODIFIED="1736015330203" TEXT="aber das kann ich nicht recht glauben">
<font ITALIC="true" NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="smiley-oh"/>
</node>
<node CREATED="1736015267192" ID="ID_140805461" MODIFIED="1736015299183">
<richcontent TYPE="NODE"><html>
<head/>
<body>
<p>
naja ... einige technische Kleinigkeiten haben noch <i>geklemmt</i>
</p>
</body>
</html>
</richcontent>
<icon BUILTIN="idea"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1735962498796" ID="ID_373701114" MODIFIED="1735962606991" TEXT="brauche nun Tests als Treiber">
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#250f69" CREATED="1736015343670" ID="ID_1628252165" MODIFIED="1736015695155" TEXT="Wichtig: die regul&#xe4;ren Builder-Operationen m&#xfc;ssen eine RValue-Ref zur&#xfc;ckgeben">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
...bisweilen hat man eben doch einen Builder (z.B. wie hier, f&#252;r Operationen <i>in dem Builder selbst</i>). Dann kann es ggfs gef&#228;hrlich sein, eine Operation auf dem Builder aufzurufen, wenn danach ein neues Objekt konstruiert wird. Wir <i>m&#252;ssen</i>&#160;ein neues Objekt konstruieren bei jedem Cross-Builder-Schritt, d.h. diese Gefahr l&#228;&#223;t sich nicht bannen. Die normalen Operationen jedoch k&#246;nnen eine RValue-Referenz zur&#252;ckgeben (analog zu lib::SeveralBuilder). Denn eine RValue-Referenz ist auch eine generelle Referen, und erlaubt den Aufruf von Member-Funktionen, so da&#223; dann <i>deren</i>&#160;R&#252;ckgabewert eigentlich bestimmt, was gebaut wird. Das ist kniffelig und kapp am Abgrund, aber nur durch einen solchen RValue-Builder sind Cross-Builder mit Weiterentwicklung der Typ-Parameter im Build-Proze&#223; m&#246;glich
</p>
</body>
</html>
</richcontent>
<linktarget COLOR="#853a40" DESTINATION="ID_1628252165" ENDARROW="Default" ENDINCLINATION="199;0;" ID="Arrow_ID_133365781" SOURCE="ID_1840237951" STARTARROW="None" STARTINCLINATION="255;18;"/>
<icon BUILTIN="broken-line"/>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1735962498796" ID="ID_373701114" MODIFIED="1736015402783" TEXT="brauche nun Tests als Treiber">
<arrowlink DESTINATION="ID_600039329" ENDARROW="Default" ENDINCLINATION="-387;16;" ID="Arrow_ID_1024063898" STARTARROW="None" STARTINCLINATION="-156;-13;"/>
<icon BUILTIN="flag-yellow"/>
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1736015405246" ID="ID_1879329039" MODIFIED="1736015411469" TEXT="NodeFeed_test">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1736015412757" ID="ID_1799619760" MODIFIED="1736015424555" TEXT="NodeBuilder_test">
<icon BUILTIN="flag-pink"/>
</node>
</node>
</node>
</node>