diff --git a/src/lib/lazy-init.hpp b/src/lib/lazy-init.hpp new file mode 100644 index 000000000..590b2a60a --- /dev/null +++ b/src/lib/lazy-init.hpp @@ -0,0 +1,106 @@ +/* + LAZY-INIT.hpp - a self-initialising functor + + Copyright (C) Lumiera.org + 2023, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file lazy-init.hpp + ** Building block to allow delayed initialisation of infrastructure tied to a functor. + ** This solution is packaged as a mix-in template and engages a hidden mechanism with + ** considerable trickery. It attempts to solve a problem arising notoriously when building + ** elaborate processing by composing functions and user-provided configuration lambdas; + ** the very point of this construction style is to tap into internal context to involve + ** deep details of the implementation without the need to represent these as structures + ** on API level. Unfortunately this has the consequence that capture-by-reference is + ** all over the place, breeding instability. The only solution to defeat this instability + ** is to lock an enclosing implementation scope into a fixed memory location, which boils + ** down to using non-copyable classes. This solution may be in conflict to the intended + ** use, especially when building DSLs, configuration frameworks or symbolic processing, + ** where entities are _value like_ from a semantic point of view. The solution pursued + ** here is to define some linkage for operational state, which allows to lock a scope + ** to a fixed memory location. Assuming that a typical usage scenario will first + ** require setup, then proceed to processing, this solution attempts to tie + ** the usage restrictions to the lifecycle — hopefully hiding the concern + ** from users sight altogether. + ** + ** # Initialisation mechanism + ** This mix-in assumes that there is a function somewhere, which activates the actual + ** processing, and this processing requires initialisation to be performed reliably + ** before first use. Thus, a _»trojan functor«_ is placed into this work-function, + ** with the goal to activate a „trap“ on first use. This allows to invoke the actual + ** initialisation, which is also configured as a functor, and which is the only part + ** the client must provide actively, to activate the mechanism. + ** + ** There is one _gory detail_ however: the initialisation hook needs the actual instance + ** pointer valid *at the time of actual initialisation*. And since initialisation shall + ** be performed automatically, the trap mechanism needs a way to derive this location, + ** relying on minimal knowledge only. This challenge can only be overcome by assuming + ** 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. + ** + ** @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 + ** on first thought. I am fully aware that »lazy initialisation« is something + ** much more generic, but I am also aware of the potential of the solution + ** coded here. Thus I'll claim that generic component name, assuming that + ** time will tell if we need a more generic framework to serve this + ** purpose eventually.... + ** @see LazyInit_test + ** @see lib::RandomDraw usage example + ** @see vault::gear::TestChainLoad::Rule where this setup matters + */ + + +#ifndef LIB_LAZY_INIT_H +#define LIB_LAZY_INIT_H + + +//#include "lib/error.h" +//#include "lib/nocopy.hpp" +#include "lib/meta/function.hpp" +//#include "lib/meta/function-closure.hpp" +//#include "lib/util-quant.hpp" +//#include "lib/util.hpp" + +//#include +//#include + + +namespace lib { +// namespace err = lumiera::error; + +// using lib::meta::_Fun; +// using std::function; +// using std::forward; +// using std::move; + + /** + * + */ + class LazyInit + { + + }; + + + +} // namespace lib +#endif /*LIB_LAZY_INIT_H*/ diff --git a/tests/15library.tests b/tests/15library.tests index 873651aa9..8f4da0140 100644 --- a/tests/15library.tests +++ b/tests/15library.tests @@ -439,6 +439,10 @@ out-lit: 42 END +PLANNED "Lazy init of a functor" LazyInit_test < + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + +/** @file lazy-init-test.cpp + ** unit test \ref LazyInit_test + */ + + + +#include "lib/test/run.hpp" +#include "lib/lazy-init.hpp" +//#include "lib/format-string.hpp" +//#include "lib/test/test-helper.hpp" +#include "lib/test/diagnostic-output.hpp" /////////////////////TODO TODOH + +//#include + + + +namespace lib { +namespace test{ + +// using util::_Fmt; +// using lib::meta::_FunRet; +// using err::LUMIERA_ERROR_LIFECYCLE; + + + + namespace { // policy and configuration for test... + + // + }//(End) Test config + + + + + + /***********************************************************************************//** + * @test Verify a mix-in to allow for lazy initialisation of complex infrastructure + * tied to a std::function; the intention is to have a »trap« hidden in the + * function itself to trigger on first use and perform the one-time + * initialisation, then finally lock the object in place. + * @see lazy-init.hpp + * @see lib::RandomDraw + */ + class LazyInit_test + : public Test + { + + void + run (Arg) + { + simpleUse(); + + verify_inlineFunctorStorage(); +// verify_numerics(); +// verify_adaptMapping(); +// verify_dynamicChange(); + } + + + + /** @test demonstrate a basic usage scenario + */ + void + simpleUse() + { + } + + + + /** @test verify that std::function indeed stores a simple functor inline + */ + void + verify_inlineFunctorStorage() + { + } + + + + }; + + + /** Register this test class... */ + LAUNCHER (LazyInit_test, "unit common"); + + +}} // namespace lib::test diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 7511eb753..0b9a9fe85 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -96209,7 +96209,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200

- + @@ -96263,7 +96263,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -96285,7 +96285,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- +
@@ -97060,10 +97060,19 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + - + @@ -97072,9 +97081,10 @@ Date:   Thu Apr 20 18:53:17 2023 +0200

+
- + @@ -97084,10 +97094,197 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + +
+ + + + + + + + + +

+ zur Anwendung müssen Funktoren gebunden sein +

+ + +
+
+ + + + + + + + + + + + + + +

+ In die eigentliche Auswertungsfunktion kann man eine »trojanische Funktion« installieren, die etwas völlig anderes macht, nämlich die Initialisierung. Danach überschreibt sie sich selbst mit der fertig gebundenen Funktion +

+ + +
+
+ + + + + + + + + + + +

+ ...mit einem gewissen Grad an Type Erasure; sie haben eine bekannte, feste Größe, egal was man reinpackt, ggfs. aber verwalten sie Heap-Storage transparent +

+ + +
+
+ + + + + + + +

+ das ist praktisch das Gleiche, beide haben eine VTable, aber der OpaqueHolder baut darauf auf und unterstützt auch Zugriff über das Basis-Interface +

+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +

+ init(this) ⟹ this wird fertig gemacht +

+ + +
+
+
+ + + + + + + + + + + + + + + + + + + + + +

+ Erfahrungswert: ein »Slot« geht immer +

+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ die Trap-Door muß irgendwo auf den Initialiser zugreifen, und dieser muß wiederum die vorläufige Funktion speichern. Alle diese Allokationen müssen direkt am Trap selber »aufgehängt« werden; wenn wir also den Trap verwerfen (durch Neuzuweisung), sägen wir den Ast ab, auf dem wir sitzen. Kann man machen, wenn man Flügel hat, oder sowieso schon auf dem Sprung ist +

+ + +
+
+ + +
+
+ + + + + + + + + + + + - - - @@ -97414,12 +97611,12 @@ Date:   Thu Apr 20 18:53:17 2023 +0200

- +
- + @@ -97429,13 +97626,35 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + +
+ + + + + + + + + + + + + + + + + + + + + +