Chain-Load: verify re-initialisation and copy

...this is a more realistic demo example, which mimics
some of the patterns present in RandomDraw. The test also
uses lambdas linking to the actual storage location, so that
the invocation would crash on a copy; LazyInit was invented
to safeguard against this, while still allowing leeway
during the initialisation phase in a DSL.
This commit is contained in:
Fischlurch 2023-11-25 03:36:19 +01:00
parent e95f729ad0
commit 04ca79fd65
3 changed files with 332 additions and 36 deletions

View file

@ -54,7 +54,7 @@
** that the »trojan functor« itself is stored somehow embedded into the target object
** to be initialised. If there is a fixed distance relation in memory, then the target
** can be derived from the self-position of the functor; if this assumption is broken
** however, memory corruption and SEGFAULT may be caused.
** however, memory corruption and SEGFAULT may be caused.
**
** @todo 11/2023 at the moment I am just desperately trying to get a bye-product of my
** main effort into usable shape and salvage an design idea that sounded clever
@ -73,15 +73,12 @@
#define LIB_LAZY_INIT_H
//#include "lib/error.h"
//#include "lib/nocopy.hpp"
#include "lib/error.h"
#include "lib/meta/function.hpp"
#include "lib/opaque-holder.hpp"
//#include "lib/meta/function-closure.hpp"
//#include "lib/util-quant.hpp"
#include "lib/util.hpp"
//#include <functional>
#include <functional>
#include <utility>
#include <memory>
@ -90,21 +87,23 @@ namespace lib {
namespace err = lumiera::error;
using lib::meta::_Fun;
using lib::meta::_FunArg;
using lib::meta::has_Sig;
// using std::function;
using util::unConst;
using std::function;
using std::forward;
using std::move;
using RawAddr = void const*;
namespace {// the anonymous namespace of horrors...
inline ptrdiff_t
captureRawAddrOffset (RawAddr anchor, RawAddr subject)
{
// Dear Mr.Compiler, please get out of my way.
// I just sincerely want to shoot myself into my foot...
// I just genuinely want to shoot myself into my foot...
char* anchorAddr = reinterpret_cast<char*> (unConst(anchor));
char* subjectAddr = reinterpret_cast<char*> (unConst(subject));
return subjectAddr - anchorAddr;
@ -140,7 +139,10 @@ namespace lib {
"apply small-object optimisation with inline storage."};
return captureRawAddrOffset (functor,payload);
}();
}
//
}//(End)low-level manipulations
/**
@ -171,12 +173,11 @@ namespace lib {
* and then to forward the invocation to the actual
* function, which should have been initialised
* by the delegate invoked.
* @param delegate a functor object pass invocation;
* @param delegate a functor object to forward invocation;
* the delegate must return a reference to the
* actual function implementation to invoke.
* Must be heap-allocated.
* @return a lightweight lambda usable as trigger.
* @note takes ownership of the delegate
*/
template<class DEL>
static auto
@ -195,10 +196,18 @@ namespace lib {
/**
*
struct EmptyBase { };
/**************************************************************//**
* Mix-in for lazy/delayed initialisation of an embedded functor.
* This allows to keep the overall object (initially) copyable,
* while later preventing copy once the functor was »engaged«.
* Initially, only a »trap« is installed into the functor,
* invoking an initialisation closure on first use.
*/
template<class PAR>
template<class PAR =EmptyBase>
class LazyInit
: public PAR
{
@ -209,8 +218,10 @@ namespace lib {
using HeapStorage = InPlaceBuffer<PlaceholderType>;
using PendingInit = std::shared_ptr<HeapStorage>;
/** manage heap storage for a pending initialisation closure */
PendingInit pendingInit_;
PendingInit const&
__trapLocked (PendingInit const& init)
{
@ -232,13 +243,27 @@ namespace lib {
}
protected:
struct MarkDisabled{};
/** @internal allows derived classes to leave the initialiser deliberately disabled */
template<typename...ARGS>
LazyInit (MarkDisabled, ARGS&& ...parentCtorArgs)
: PAR(forward<ARGS> (parentCtorArgs)...)
, pendingInit_{}
{ }
public:
/** prepare an initialiser to be activated on first use */
template<class SIG, class INI, typename...ARGS>
LazyInit (std::function<SIG>& targetFunctor, INI&& initialiser, ARGS&& ...parentCtorArgs)
: PAR(forward<ARGS> (parentCtorArgs)...)
, pendingInit_{prepareInitialiser (targetFunctor, forward<INI> (initialiser))}
{ }
LazyInit (LazyInit const& ref)
: PAR{ref}
, pendingInit_{__trapLocked (ref.pendingInit_)}
@ -272,6 +297,12 @@ namespace lib {
}
bool
isInit() const
{
return not pendingInit_;
}
template<class SIG>
void
installEmptyInitialiser()
@ -279,7 +310,15 @@ namespace lib {
pendingInit_.reset (new HeapStorage{emptyInitialiser<SIG>()});
}
private:
template<class SIG, class INI>
void
installInitialiser (std::function<SIG>& targetFunctor, INI&& initialiser)
{
pendingInit_ = prepareInitialiser (targetFunctor, forward<INI> (initialiser));
}
private: /* ========== setup of the initialisation mechanism ========== */
template<class SIG>
DelegateType<SIG>
emptyInitialiser()
@ -306,7 +345,7 @@ namespace lib {
template<class SIG>
DelegateType<SIG>*
getPointerToDelegate(HeapStorage& buffer)
getPointerToDelegate (HeapStorage& buffer)
{
return reinterpret_cast<DelegateType<SIG>*> (&buffer);
}
@ -316,6 +355,7 @@ namespace lib {
buildInitialiserDelegate (std::function<SIG>& targetFunctor, INI&& initialiser)
{
using TargetFun = std::function<SIG>;
using ExpectedArg = _FunArg<INI>;
return DelegateType<SIG>{
[performInit = forward<INI> (initialiser)
,targetOffset = captureRawAddrOffset (this, &targetFunctor)]
@ -324,9 +364,10 @@ namespace lib {
TargetFun* target = relocate<TargetFun> (location, -FUNCTOR_PAYLOAD_OFFSET);
LazyInit* self = relocate<LazyInit> (target, -targetOffset);
REQUIRE (self);
performInit (self);
self->pendingInit_.reset();
return *target;
// invoke init, possibly downcast to derived *self
performInit (static_cast<ExpectedArg> (self));
self->pendingInit_.reset(); // release storage
return *target; // invoked by the »Trojan« to yield first result
}};
}
};

View file

@ -34,7 +34,7 @@
#include "lib/test/diagnostic-output.hpp" /////////////////////TODO TODOH
#include "lib/util.hpp"
//#include <array>
#include <memory>
@ -42,10 +42,10 @@ namespace lib {
namespace test{
// using util::_Fmt;
using std::make_unique;
using util::isSameObject;
using lib::meta::isFunMember;
// using lib::meta::_FunRet;
// using err::LUMIERA_ERROR_LIFECYCLE;
using err::LUMIERA_ERROR_LIFECYCLE;
@ -78,6 +78,7 @@ namespace test{
verify_TargetRelocation();
verify_triggerMechanism();
verify_lazyInitialisation();
verify_complexUsageWithCopy();
}
@ -87,7 +88,7 @@ namespace test{
* # the _target function_ finally to be invoked performs a verifiable computation
* # the _delegate_ receives an memory location and returns a reference to the target
* # the generated _»trojan λ«_ captures its own address, invokes the delegate,
* retrieves a reference to a target functor, and invokes these with actual arguments.
* retrieves a reference to a target functor, and finally invokes this with actual arguments.
* @remark the purpose of this convoluted scheme is for the _delegate to perform initialisation,_
* taking into account the current memory location sniffed by the trojan.
*/
@ -119,16 +120,15 @@ namespace test{
return fun;
};
using Delegate = decltype(delegate);
Delegate *delP = new Delegate(delegate);
auto delP = make_unique<Delegate> (delegate);
// verify the heap-allocated copy of the delegate behaves as expected
location = nullptr;
CHECK (beacon+c == (*delP)(this)(c));
CHECK (location == this);
// now (finally) build the »trap function«,
// taking ownership of the heap-allocated delegate copy
auto trojanLambda = TrojanFun<Sig>::generateTrap (delP);
// now (finally) build the »trap function«...
auto trojanLambda = TrojanFun<Sig>::generateTrap (delP.get());
CHECK (sizeof(trojanLambda) == sizeof(size_t));
// on invocation...
@ -138,7 +138,7 @@ namespace test{
CHECK (beacon+c == trojanLambda(c));
CHECK (location == &trojanLambda);
// repeat that with a copy, and changed beacon value
// repeat same with a copy, and changed beacon value
auto trojanClone = trojanLambda;
beacon = rand();
c = beacon % 55;
@ -150,17 +150,17 @@ namespace test{
/** @test verify that std::function indeed stores a simple functor inline
/** @test verify that std::function indeed stores a simple functor inline.
* @remark The implementation of LazyInit relies crucially on a known optimisation
* in the standard library which unfortunately is not guaranteed by the standard:
* Typically, std::function will apply _small object optimisation_ to place a very
* small functor directly into the wrapper, if the payload has a trivial copy-ctor.
* Libstdc++ is known to be rather restrictive, other implementations trade increased
* storage size of std::function against more optimisation possibilities.
* `Libstdc++` is known to be rather restrictive, while other implementations trade
* increased storage size of std::function against more optimisation possibilities.
* LazyInit exploits this optimisation to spy about the current object location,
* to allow executing the lazy initialisation on first use, without further help
* allowing to execute the lazy initialisation on first use, without further help
* by client code. This trickery seems to be the only way, since λ-capture by reference
* is broken after copying or moving the host object (which is required for DSL use).
* is broken after copying or moving the host object (typically required for DSL use).
* In case this turns out to be fragile, LazyInit should become a "LateInit" and needs
* help by the client or the user to trigger initialisation; alternatively the DSL
* could be split off into a separate builder object distinct from RandomDraw.
@ -169,7 +169,7 @@ namespace test{
verify_inlineStorage()
{
// char payload[24];// ◁─────────────────────────────── use this to make the test fail....
const char* payload = "Outer Space";
const char* payload = "please look elsewhere";
auto lambda = [payload]{ return RawAddr(&payload); };
RawAddr location = lambda();
@ -196,7 +196,7 @@ namespace test{
* by applying known offsets consecutively
* from a starting point within an remote instance
* @remark in the real usage scenario, we know _only_ the offset
* and and attempt to find home without knowing the layout.
* and attempt to find home without knowing the layout.
*/
void
verify_TargetRelocation()
@ -229,7 +229,7 @@ namespace test{
CHECK (offNested > 0);
// create a copy far far away...
auto farAway = std::make_unique<Demo> (here);
auto farAway = make_unique<Demo> (here);
// reconstruct base address from starting point
RawAddr startPoint = farAway->peek();
@ -322,6 +322,89 @@ namespace test{
CHECK (1 == invoked);
CHECK (init);
}
/** elaborate setup used for integration test */
struct LazyDemo
: LazyInit<>
{
using Fun = std::function<int(int)>;
int seed{0};
Fun fun; // ◁────────────────────────────────── this will be initialised lazily....
template<typename FUN>
auto
buildInit (FUN&& fun2install)
{
return [theFun = forward<FUN> (fun2install)]
(LazyDemo* self)
{
CHECK (self);
self->fun = [self, chain = move(theFun)]
(int i)
{
return chain (i + self->seed); // Note: binding to actual instance location
};
};
}
LazyDemo()
: LazyInit{MarkDisabled()}
, fun{}
{
installInitialiser(fun, buildInit([](int){ return 0; }));
}
template<typename FUN>
LazyDemo(FUN&& someFun)
: LazyInit{MarkDisabled()}
, fun{}
{
installInitialiser(fun, buildInit (forward<FUN> (someFun)));
}
};
/**
* @test use an elaborately constructed example to cover more corner cases
* - the function to manage and initialise lazily is _a member_ of the _derived class_
* - the initialisation routine _adapts_ this function and links it with the current
* object location; thus, invoking this function on a copy would crash / corrupt memory.
* - however, as long as initialisation has not been triggered, LazyDemo instances can be
* copied; they may even be assigned to existing instances, overwriting their state.
*/
void
verify_complexUsageWithCopy()
{
LazyDemo d1;
CHECK (not d1.isInit()); // not initialised, since function was not invoked yet
CHECK (d1.fun); // the functor is not empty anymore, since the »trap« was installed
d1.seed = 2;
CHECK (0 == d1.fun(22)); // d1 was default initialised and thus got the "return 0" function
CHECK (d1.isInit()); // first invocation also triggered the init-routine
// is »engaged« after init and rejects move / copy
VERIFY_ERROR (LIFECYCLE, LazyDemo dx{move(d1)} );
d1 = LazyDemo{[](int i) // assign a fresh copy (discarding any state in d1)
{
return i + 1; // using a "return i+1" function
}};
CHECK (not d1.isInit());
CHECK (d1.seed == 0); // assignment indeed erased any existing settings (seed≔2)
CHECK (d1.fun);
CHECK (23 == d1.fun(22)); // new function was tied in (while also referring to self->seed)
CHECK (d1.isInit());
d1.seed = 3; // set the seed
CHECK (26 == d1.fun(22)); // seed value is picked up dynamically
VERIFY_ERROR (LIFECYCLE, LazyDemo dx{move(d1)} );
}
};

View file

@ -97280,6 +97280,93 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1700779275391" ID="ID_1990626821" MODIFIED="1700792011791" TEXT="LazyInit_test.cpp">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1700875396864" ID="ID_1861612135" MODIFIED="1700875416597" TEXT="verify_trojanLambda">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1700875396864" FOLDED="true" ID="ID_1355973273" MODIFIED="1700875456048" TEXT="verify_inlineStorage">
<icon BUILTIN="button_ok"/>
<node CREATED="1700875427276" ID="ID_403717073" MODIFIED="1700875442853" TEXT="Untersuchungen zum Verhalten von std::function (libStc++8)"/>
<node CREATED="1700875443865" ID="ID_1990486288" MODIFIED="1700875455003" TEXT="entwickle ein Pr&#xe4;dikat um Heap-Storage zu erkennen"/>
</node>
<node COLOR="#338800" CREATED="1700875396865" FOLDED="true" ID="ID_1084080047" MODIFIED="1700875514743" TEXT="verify_TargetRelocation">
<icon BUILTIN="button_ok"/>
<node CREATED="1700875464620" ID="ID_758182999" MODIFIED="1700875483607" TEXT="spiele die grauenhafte Pointer-Arrithmetik mal im Labor durch"/>
<node CREATED="1700875484403" ID="ID_52550971" MODIFIED="1700875504989" TEXT="verwende bewu&#xdf;t Beispiel mit normalen Structs, um Offsets jenseits der Slots zu bekommen"/>
<node CREATED="1700875505873" ID="ID_1289960190" MODIFIED="1700875513435" TEXT="funktioniert, auch mit einem halb-Offset"/>
</node>
<node COLOR="#338800" CREATED="1700875396865" FOLDED="true" ID="ID_1339757746" MODIFIED="1700875579772" TEXT="verify_triggerMechanism">
<icon BUILTIN="button_ok"/>
<node CREATED="1700875518455" ID="ID_1980265417" MODIFIED="1700875542195" TEXT="baue f&#xfc;r mein eigenes Verst&#xe4;ndnis nochmal den Trigger-Mechanismus zu fu&#xdf; nach"/>
<node CREATED="1700875543520" ID="ID_92771542" MODIFIED="1700875550945" TEXT="k&#xf6;nnte auch zur Dokumentation hilfreich sein"/>
<node CREATED="1700875554730" ID="ID_1759963840" MODIFIED="1700875577811" TEXT="(an den eigentlichen Trigger kommt man nicht ran, da der zu fest mit LazyInit verkopelt ist)"/>
</node>
<node COLOR="#338800" CREATED="1700875396865" FOLDED="true" ID="ID_407572310" MODIFIED="1700875618722" TEXT="verify_lazyInitialisation">
<icon BUILTIN="button_ok"/>
<node CREATED="1700875581782" ID="ID_902572155" MODIFIED="1700875588657" TEXT="spiele ein bewu&#xdf;t einfaches Beispiel durch"/>
<node CREATED="1700875589219" ID="ID_831881033" MODIFIED="1700875597848" TEXT="LazyInit direkt als top-level-Klasse"/>
<node CREATED="1700875598444" ID="ID_373137353" MODIFIED="1700875604078" TEXT="erbt direkt von einer function"/>
<node CREATED="1700875605831" ID="ID_422784862" MODIFIED="1700875617830" TEXT="pr&#xfc;fe, da&#xdf; der Initialiser nur einmal l&#xe4;uft"/>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1700875396866" ID="ID_1176534247" MODIFIED="1700875620954" TEXT="verify_complexUsageWithCopy">
<icon BUILTIN="pencil"/>
<node CREATED="1700875626785" ID="ID_1403498874" MODIFIED="1700875642770" TEXT="baue bewu&#xdf;t ein m&#xf6;glichst verschlungenes (realisitisches) Beispiel"/>
<node CREATED="1700875643550" ID="ID_1209015029" MODIFIED="1700875657152" TEXT="macht eine nicht-triviale Verknp&#xfc;fung, d.h. adaptiert die gegebene Funktion noch"/>
<node CREATED="1700875658155" ID="ID_278966157" MODIFIED="1700875690711">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
die Funktion ist dieses Mal <b>ein Feld</b>&#160;<i>im </i>abgeleiteten Objekt (yess!)
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1700875716980" ID="ID_649768460" MODIFIED="1700875737214" TEXT="default-ctor installiert fallback-Funkction"/>
<node CREATED="1700875738346" ID="ID_1264877418" MODIFIED="1700875746853" TEXT="ein ctor mit explizit gegebener Function"/>
<node CREATED="1700875748355" ID="ID_103299394" MODIFIED="1700875753491" TEXT="Testf&#xe4;lle">
<node COLOR="#338800" CREATED="1700875754418" ID="ID_660767780" MODIFIED="1700878189835" TEXT="default-Objekt">
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1700875764710" ID="ID_695451647" MODIFIED="1700878192178" TEXT="Zustand nach Konstruktion">
<icon BUILTIN="button_ok"/>
<node COLOR="#435e98" CREATED="1700875806915" ID="ID_1651702944" MODIFIED="1700878163584" TEXT="Function-Feld ist nicht initialisiert">
<icon BUILTIN="broken-line"/>
<node CREATED="1700875834493" ID="ID_1564551745" MODIFIED="1700875839823" TEXT="not d1.fun"/>
<node CREATED="1700875841150" ID="ID_88050393" MODIFIED="1700875849677" TEXT="throws invalid_function_call"/>
<node COLOR="#435e98" CREATED="1700875853129" ID="ID_502226878" MODIFIED="1700878147483" TEXT="Analyse(debugger)">
<icon BUILTIN="list"/>
<node CREATED="1700876345072" ID="ID_1248014496" MODIFIED="1700876355643" TEXT="Ha! Feld wird nach Basisklasse initialisiert"/>
<node CREATED="1700876356351" ID="ID_1118675302" MODIFIED="1700876372841" TEXT="d.h. der bereits platzierte Trojaner wird &#xfc;berschrieben"/>
<node CREATED="1700876373789" ID="ID_1710398995" MODIFIED="1700876376930" TEXT="oh wie schade"/>
</node>
<node COLOR="#435e98" CREATED="1700876399272" ID="ID_96046426" MODIFIED="1700878150904" TEXT="&#x27f9; brauche">
<node CREATED="1700876408887" ID="ID_1752186900" MODIFIED="1700876416771" TEXT="Leer-Initialisierung von LazyInit"/>
<node CREATED="1700876426498" ID="ID_437510896" MODIFIED="1700876503102" TEXT="Setter f&#xfc;r sp&#xe4;teres Installieren">
<arrowlink COLOR="#4d5b96" DESTINATION="ID_932710079" ENDARROW="Default" ENDINCLINATION="776;-58;" ID="Arrow_ID_1938279623" STARTARROW="None" STARTINCLINATION="-349;33;"/>
</node>
</node>
</node>
<node COLOR="#338800" CREATED="1700878155031" ID="ID_980233097" MODIFIED="1700878157482" TEXT="tut">
<icon BUILTIN="button_ok"/>
</node>
</node>
<node COLOR="#338800" CREATED="1700875772636" ID="ID_1469062137" MODIFIED="1700878173636" TEXT="aufrufen : default-Function l&#xe4;uft">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1700875784147" ID="ID_542765389" MODIFIED="1700878175169" TEXT="Zustand danach">
<icon BUILTIN="button_ok"/>
</node>
</node>
<node COLOR="#338800" CREATED="1700875788327" ID="ID_1387936235" MODIFIED="1700878176498" TEXT="weise ein Objekt mit explizit gebundener Funktion zu">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1700878177787" ID="ID_707719092" MODIFIED="1700878185308" TEXT="engaged / reject copy">
<icon BUILTIN="button_ok"/>
</node>
</node>
</node>
</node>
</node>
<node COLOR="#338800" CREATED="1700786308827" ID="ID_627670298" MODIFIED="1700865645701" TEXT="der Trojanische-Funktor">
@ -97451,12 +97538,97 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="button_ok"/>
</node>
</node>
<node COLOR="#338800" CREATED="1700871032790" ID="ID_1056099589" MODIFIED="1700878109349" TEXT="bereits installierten Initialiser ersetzen">
<icon BUILTIN="button_ok"/>
<node CREATED="1700871049276" ID="ID_932710079" MODIFIED="1700876495342" TEXT="in der Tat ... das kann vorkommen">
<linktarget COLOR="#4d5b96" DESTINATION="ID_932710079" ENDARROW="Default" ENDINCLINATION="776;-58;" ID="Arrow_ID_1938279623" SOURCE="ID_437510896" STARTARROW="None" STARTINCLINATION="-349;33;"/>
<icon BUILTIN="yes"/>
</node>
<node CREATED="1700871060514" ID="ID_600057244" MODIFIED="1700871092594" TEXT="sollte der realisierte Mechanismus auch k&#xf6;nnen...">
<node CREATED="1700871098989" ID="ID_700052304" MODIFIED="1700871109959" TEXT="und zwar macht hier shared_ptr die ganze Magie"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1700871113915" ID="ID_1741357094" MODIFIED="1700871131876" TEXT="die Initialisierungen k&#xf6;nnen sich durchaus verzweigen">
<icon BUILTIN="messagebox_warning"/>
</node>
</node>
<node COLOR="#338800" CREATED="1700871143583" ID="ID_1088454070" MODIFIED="1700878102782" TEXT="mu&#xdf; dann aber eine Setter-Funktion public machen">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1700876522892" ID="ID_199433026" MODIFIED="1700878099429" TEXT="brauche dann auch einen Leer-Init-Zustand">
<icon BUILTIN="button_ok"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1700878072770" ID="ID_1728628936" MODIFIED="1700878084969" TEXT="Prolbem: ctor-Signatur zweideutig">
<icon BUILTIN="messagebox_warning"/>
</node>
<node CREATED="1700878086446" ID="ID_1368715362" MODIFIED="1700878097543" TEXT="l&#xf6;sen durch ein MarkDisabled() - Tag">
<icon BUILTIN="idea"/>
</node>
</node>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1700871279919" ID="ID_1126055663" MODIFIED="1700871683297">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
hier besteht ein latentes semantsiches Problem:
</p>
<p>
lazyInit &#10233; Objekt ist erst mal <i>noch nicht ganz</i>&#160;initialisiert
</p>
</body>
</html>
</richcontent>
<arrowlink COLOR="#a11470" DESTINATION="ID_615420321" ENDARROW="Default" ENDINCLINATION="-38;-156;" ID="Arrow_ID_1147775137" STARTARROW="None" STARTINCLINATION="-409;35;"/>
<icon BUILTIN="messagebox_warning"/>
</node>
</node>
</node>
<node COLOR="#c1019e" CREATED="1700856585458" ID="ID_373199802" MODIFIED="1700856611800" TEXT="ich f&#xfc;hl mich schei&#xdf;e">
<font NAME="SansSerif" SIZE="16"/>
<icon BUILTIN="smily_bad"/>
</node>
</node>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1700871359629" ID="ID_615420321" MODIFIED="1700871683297" TEXT="kann nicht alle Usage-Pattern abdecken">
<linktarget COLOR="#a11470" DESTINATION="ID_615420321" ENDARROW="Default" ENDINCLINATION="-38;-156;" ID="Arrow_ID_1147775137" SOURCE="ID_1126055663" STARTARROW="None" STARTINCLINATION="-409;35;"/>
<icon BUILTIN="broken-line"/>
<node CREATED="1700871415082" ID="ID_1938566813" MODIFIED="1700871510648" TEXT="es geht allerdings um ein ehr fortgeschrittenes Szenario">
<icon BUILTIN="info"/>
<node CREATED="1700871453605" ID="ID_1763209918" MODIFIED="1700871474745" TEXT="Installieren einer dynamischen Manipulation &#xfc;ber ein bestehendes Mapping">
<icon BUILTIN="forward"/>
</node>
<node CREATED="1700871479242" ID="ID_269811751" MODIFIED="1700871501010" TEXT="es funktioniert, sofern man die erste Konfiguration tats&#xe4;chlich einmal aktiviert">
<icon BUILTIN="idea"/>
</node>
<node CREATED="1700871434600" ID="ID_211010484" MODIFIED="1700871446348" TEXT="im Test hab ich das, aber ob es jemals gebraucht wird?"/>
</node>
<node CREATED="1700871595595" ID="ID_370281479" MODIFIED="1700871614083" TEXT="es lie&#xdf;e sich allerdings durchaus realisieren">
<icon BUILTIN="smiley-neutral"/>
<node CREATED="1700871621703" ID="ID_350569158" MODIFIED="1700871630435" TEXT="und zwar, indem man Initialisierer verkettet"/>
<node CREATED="1700871631338" ID="ID_382997669" MODIFIED="1700878229737" TEXT="iz werds aba wuid">
<icon BUILTIN="smiley-angry"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1700871688167" ID="ID_393112560" MODIFIED="1700871731833" TEXT="warum sehe ich immer sofort noch eine L&#xf6;sung??">
<icon BUILTIN="smiley-oh"/>
<icon BUILTIN="smily_bad"/>
</node>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1700871698421" ID="ID_856751270" MODIFIED="1700871709596" TEXT="warum gebe ich mich nicht l&#xe4;ngst geschlagen???">
<icon BUILTIN="help"/>
<node CREATED="1700871745466" ID="ID_143069674" MODIFIED="1700871754102" TEXT="die Lektion ist inwsichen sowas von klar"/>
<node CREATED="1700871754718" ID="ID_845938579" MODIFIED="1700871770128">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
dieses Design ist <b>MIST</b>
</p>
</body>
</html>
</richcontent>
</node>
</node>
</node>
</node>
</node>
</node>
</node>
<node COLOR="#338800" CREATED="1700577811739" ID="ID_1356052727" MODIFIED="1700623391805" TEXT="Konfigurations-DSL aufbauen">