Lumiera
0.pre.03
»edit your freedom«
|
Go to the source code of this file.
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.
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. Several initialisation steps can be attached consecutively, and will later be triggered in sequence.
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 might ensue. These assumptions are covered by an assertion and unit tests; as long as the function and the LazyInit instance are arranged in a fixed memory layout, this scheme should work. Do not place one or the other into a virtual base class though.
Definition in file lazy-init.hpp.
#include "lib/error.h"
#include "lib/meta/function.hpp"
#include "lib/opaque-holder.hpp"
#include "lib/util.hpp"
#include <functional>
#include <utility>
#include <memory>
Classes | |
struct | EmptyBase |
class | LazyInit< PAR > |
Mix-in for lazy/delayed initialisation of an embedded functor. More... | |
struct | LazyInit< PAR >::MarkDisabled |
class | TrojanFun< SIG > |
»Trojan Function« builder. More... | |
Typedefs | |
using | RawAddr = void const * |
Functions | |
ptrdiff_t | captureRawAddrOffset (RawAddr anchor, RawAddr subject) |
template<class TAR > | |
static TAR * | relocate (RawAddr anchor, ptrdiff_t offset) |
Variables | |
const ptrdiff_t | FUNCTOR_PAYLOAD_OFFSET |
Namespaces | |
lib | |
Implementation namespace for support and library code. | |
struct lib::LazyInit::MarkDisabled |