From ed8d9939bd0ba5dd801a075136bc7ee796c15d1c Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 25 Nov 2023 17:18:26 +0100 Subject: [PATCH] Chain-Load: provide a scheme for repeated init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For context: I've engaged into writing a `LazyInit` helper component, to resolve the inner contradiction between DSL use of `RandomDraw` (implying value semantics) and the design of a processing pipeline, which quite naturally leads to binding by reference into the enclosing implementation. In most cases, this change (to lazy on-demand initialisation) should be transparent for the complete implementation code in `RandomDraw` -- with one notable exception: when configuring an elaborate pipeline, especially with dynamic changes of the probability profile during the simulation run, then then obviously there is the desire to use the existing processing pipeline from the reconfiguration function (in fact it would be quite hard to explain why and where this should be avoided). `LazyInit` breaks this usage scenario, since -- at the time the reconfiguration runs -- now the object is not initialised at all, but holds a »Trojan« functor, which will trigger initialisation eventually. After some headaches and grievances (why am I engaging into such an elaborate solution for such an accidental and marginal topic...), unfortunately it occurred to me that even this problem can be fixed, with yet some further "minimal" adjustments to the scheme: the LazyInit mechanism ''just needs to ensure'' that the init-functor ''sees the same environment as in eager init'' -- that is, it must clear out the »Trojan« first, and it ''could apply any previous pending init function'' fist. That is, with just a minimal change, we possibly build a chain of init functors now, and apply them in given order, so each one sees the state the previous one created -- as if this was just direct eager object manipulation... --- src/lib/lazy-init.hpp | 22 +++++- wiki/thinkPad.ichthyo.mm | 162 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 182 insertions(+), 2 deletions(-) diff --git a/src/lib/lazy-init.hpp b/src/lib/lazy-init.hpp index 4d3107a48..e8d6e296d 100644 --- a/src/lib/lazy-init.hpp +++ b/src/lib/lazy-init.hpp @@ -335,6 +335,13 @@ namespace lib { PendingInit prepareInitialiser (std::function& targetFunctor, INI&& initialiser) { + if (isInit() and targetFunctor) + {// object is already »engaged« — no need to delay init + using ExpectedArg = _FunArg; + initialiser (static_cast (this)); + return PendingInit(); // keep engaged; no pending init + } + // else: prepare delayed init... PendingInit storageHandle{ new HeapStorage{ buildInitialiserDelegate (targetFunctor, forward (initialiser))}}; @@ -344,12 +351,22 @@ namespace lib { } template - DelegateType* + static DelegateType* getPointerToDelegate (HeapStorage& buffer) { return reinterpret_cast*> (&buffer); } + template + static std::function + maybeInvoke (PendingInit const& pendingInit, RawAddr location) + { + if (not pendingInit) // no pending init -> empty TargetFun + return std::function(); + auto* pendingDelegate = getPointerToDelegate(*pendingInit); + return (*pendingDelegate) (location); // invoke to create new TargetFun + } + template DelegateType buildInitialiserDelegate (std::function& targetFunctor, INI&& initialiser) @@ -358,12 +375,15 @@ namespace lib { using ExpectedArg = _FunArg; return DelegateType{ [performInit = forward (initialiser) + ,previousInit = move (pendingInit_) ,targetOffset = captureRawAddrOffset (this, &targetFunctor)] (RawAddr location) -> TargetFun& {// apply known offset backwards to find current location of the host object TargetFun* target = relocate (location, -FUNCTOR_PAYLOAD_OFFSET); LazyInit* self = relocate (target, -targetOffset); REQUIRE (self); + // setup target as it would be with eager init + (*target) = maybeInvoke (previousInit, location); // invoke init, possibly downcast to derived *self performInit (static_cast (self)); self->pendingInit_.reset(); // release storage diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 0fff39895..89c474778 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -97558,7 +97558,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -97579,6 +97579,17 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + + + @@ -97628,6 +97639,155 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ darauf kommt es jetzt auch nicht mehr an +

+ + +
+
+ + + + + + +

+ Funktor ist Funktor! +

+

+ Deshalb sind sie ja stateless (und müssen auch so bleiben, also nicht mutable). Für jeweils eine Instanz wird ein Funktor einfach »verbrannt«, indem man ihn aufruft und danach das pendingInit leert. Und zwar auf *self  — der Funktor als Solcher bleibt stateless. Wenn ihn noch jemand anders referenziert, dann schön.... +

+ + +
+
+ + + + + + +

+ Beim Umkonfigurieren wird der pendingInit verschoben +

+ + +
+ + + + + +

+ und zwar aus dem LazyInit-Objekt in den Adapter hinein. Der use-count bleibt dadurch gleich. +

+ + +
+
+
+ + + +