Lumiera 0.pre.04
»edit your freedom«
Loading...
Searching...
No Matches
lazy-init.hpp
Go to the documentation of this file.
1/*
2 LAZY-INIT.hpp - a self-initialising functor
3
4 Copyright (C)
5 2023, Hermann Vosseler <Ichthyostega@web.de>
6
7  **Lumiera** is free software; you can redistribute it and/or modify it
8  under the terms of the GNU General Public License as published by the
9  Free Software Foundation; either version 2 of the License, or (at your
10  option) any later version. See the file COPYING for further details.
11
12*/
13
67#ifndef LIB_LAZY_INIT_H
68#define LIB_LAZY_INIT_H
69
70
71#include "lib/error.h"
72#include "lib/meta/function.hpp"
73#include "lib/opaque-holder.hpp"
74#include "lib/util.hpp"
75
76#include <functional>
77#include <utility>
78#include <memory>
79
80
81namespace lib {
82 namespace err = lumiera::error;
83
84 using lib::meta::_Fun;
87 using util::unConst;
88 using std::function;
89 using std::forward;
90 using std::move;
91
92 using RawAddr = void const*;
93
94
95 namespace {// the anonymous namespace of horrors...
96
97 inline ptrdiff_t
99 {
100 // Dear Mr.Compiler, please get out of my way.
101 // I just genuinely want to shoot myself into my foot...
102 char* anchorAddr = reinterpret_cast<char*> (unConst(anchor));
103 char* subjectAddr = reinterpret_cast<char*> (unConst(subject));
104 return subjectAddr - anchorAddr;
105 }
106
107 template<class TAR>
108 inline static TAR*
109 relocate (RawAddr anchor, ptrdiff_t offset)
110 {
111 char* anchorAddr = reinterpret_cast<char*> (unConst(anchor));
112 char* adjusted = anchorAddr + offset;
113 void* rawTarget = reinterpret_cast<void*> (adjusted);
114 return static_cast<TAR*> (rawTarget);
115 }
116
117
126 const ptrdiff_t FUNCTOR_PAYLOAD_OFFSET =
127 []{
128 size_t slot{42};
129 std::function<RawAddr(void)> probe = [slot]{ return RawAddr(&slot); };
130 RawAddr functor = &probe;
131 RawAddr payload = probe();
132 if (not util::isCloseBy(functor, payload))
133 throw err::Fatal{"Unable to use lib::LazyInit because std::function does not "
134 "apply small-object optimisation with inline storage."};
135 return captureRawAddrOffset (functor,payload);
136 }();
137 //
138 }//(End)low-level manipulations
139
140
141
142
148 template<class SIG>
150 {
151 template<class DEL, typename RET, typename... ARGS>
152 static auto
153 buildTrapActivator (DEL* delegate, _Fun<RET(ARGS...)>)
154 {
155 return [delegate]
156 (ARGS ...args) -> RET
157 {
158 auto currLocation = &delegate;
159 auto& functor = (*delegate) (currLocation);
160 //
161 return functor (forward<ARGS> (args)...);
162 };
163 }
164 public:
165
177 template<class DEL>
178 static auto
179 generateTrap (DEL* delegate)
180 {
181 static_assert (_Fun<DEL>(), "Delegate must be function-like");
182 using Ret = _Fun<DEL>::Ret;
183 static_assert (_Fun<Ret>(), "Result from invoking delegate must also be function-like");
184 static_assert (has_Sig<Ret, SIG>(), "Result from delegate must expose target signature");
185
186 REQUIRE (delegate);
187
188 return buildTrapActivator (delegate, _Fun<SIG>());
189 }
190 };
191
192
193
194
195
196 struct EmptyBase { };
197
198 /**************************************************************/
205 template<class PAR =EmptyBase>
207 : public PAR
208 {
209 template<class SIG>
210 using DelegateType = std::function<std::function<SIG>&(RawAddr)>;
211
212 using PlaceholderType = DelegateType<void(void)>;
214 using PendingInit = std::shared_ptr<HeapStorage>;
215
218
219
220 PendingInit const&
222 {
223 if (not init)
224 throw err::State{"Component was already configured with a processing function, "
225 "which binds into a fixed object location. It can not be moved anymore."
226 , err::LUMIERA_ERROR_LIFECYCLE};
227 return init;
228 }
229
232 {
233 if (not init)
234 throw err::State{"Component was already configured with a processing function, "
235 "which binds into a fixed object location. It can not be moved anymore."
236 , err::LUMIERA_ERROR_LIFECYCLE};
237 return move (init);
238 }
239
240
241
242 protected:
243 struct MarkDisabled{};
244
246 template<typename...ARGS>
247 LazyInit (MarkDisabled, ARGS&& ...parentCtorArgs)
248 : PAR(forward<ARGS> (parentCtorArgs)...)
249 , pendingInit_{}
250 { }
251
252
253 public:
255 template<class SIG, class INI, typename...ARGS>
256 LazyInit (std::function<SIG>& targetFunctor, INI&& initialiser, ARGS&& ...parentCtorArgs)
257 : PAR(forward<ARGS> (parentCtorArgs)...)
258 , pendingInit_{}
259 {
260 installInitialiser (targetFunctor, forward<INI> (initialiser));
261 }
262
263
264 LazyInit (LazyInit const& ref)
265 : PAR{ref}
267 { }
268
270 : PAR{move (rref)}
272 { }
273
274 LazyInit&
276 {
277 if (not util::isSameObject (ref, *this))
278 {
279 PAR::operator= (ref);
281 }
282 return *this;
283 }
284
285 LazyInit&
287 {
288 if (not util::isSameObject (rref, *this))
289 {
290 PAR::operator= (move (rref));
291 pendingInit_ = __trapLocked (move (rref.pendingInit_));
292 }
293 return *this;
294 }
295
296
297 bool
298 isInit() const
299 {
300 return not pendingInit_;
301 }
302
303 template<class SIG>
304 void
306 {
307 pendingInit_.reset (new HeapStorage{emptyInitialiser<SIG>()});
308 }
309
310 template<class SIG, class INI>
311 void
312 installInitialiser (std::function<SIG>& targetFunctor, INI&& initialiser)
313 {
314 pendingInit_ = prepareInitialiser (targetFunctor, forward<INI> (initialiser));
315 }
316
317
318 private: /* ========== setup of the initialisation mechanism ========== */
319 template<class SIG>
320 DelegateType<SIG>
322 {
323 using TargetFun = std::function<SIG>;
324 return DelegateType<SIG>([disabledFunctor = TargetFun()]
325 (RawAddr) -> TargetFun&
326 {
327 return disabledFunctor;
328 });
329 }
330
331 template<class SIG, class INI>
333 prepareInitialiser (std::function<SIG>& targetFunctor, INI&& initialiser)
334 {
335 if (isInit() and targetFunctor)
336 {// object is already »engaged« — no need to delay init
337 using ExpectedArg = _FunArg<INI>;
338 initialiser (static_cast<ExpectedArg> (this));
339 return PendingInit(); // keep engaged; no pending init
340 }
341 // else: prepare delayed init...
342 PendingInit storageHandle{
343 new HeapStorage{
344 buildInitialiserDelegate (targetFunctor, forward<INI> (initialiser))}};
345 // place a »Trojan« into the target functor to trigger initialisation on invocation...
346 targetFunctor = TrojanFun<SIG>::generateTrap (getPointerToDelegate<SIG> (*storageHandle));
347 return storageHandle;
348 }
349
350 template<class SIG>
351 static DelegateType<SIG>*
353 {
354 return reinterpret_cast<DelegateType<SIG>*> (&buffer);
355 }
356
357 template<class SIG>
358 static std::function<SIG>
359 maybeInvoke (PendingInit const& pendingInit, RawAddr location)
360 {
361 if (not pendingInit) // no pending init -> empty TargetFun
362 return std::function<SIG>();
363 auto* pendingDelegate = getPointerToDelegate<SIG>(*pendingInit);
364 return (*pendingDelegate) (location); // invoke to create new TargetFun
365 }
366
367 template<class SIG, class INI>
368 DelegateType<SIG>
369 buildInitialiserDelegate (std::function<SIG>& targetFunctor, INI&& initialiser)
370 {
371 using TargetFun = std::function<SIG>;
372 using ExpectedArg = _FunArg<INI>;
373 return DelegateType<SIG>{
374 [performInit = forward<INI> (initialiser)
375 ,previousInit = move (pendingInit_)
376 ,targetOffset = captureRawAddrOffset (this, &targetFunctor)]
377 (RawAddr location) -> TargetFun&
378 {// apply known offset backwards to find current location of the host object
379 TargetFun* target = relocate<TargetFun> (location, -FUNCTOR_PAYLOAD_OFFSET);
380 LazyInit* self = relocate<LazyInit> (target, -targetOffset);
381 REQUIRE (self);
382 // tie storage to this (possibly recursive) call
383 auto storageHandle = move(self->pendingInit_);
384 // setup target as it would be with eager init
385 (*target) = maybeInvoke<SIG> (previousInit, location);
386 // invoke init, possibly downcast to derived *self
387 performInit (static_cast<ExpectedArg> (self));
388 return *target; // back to the »Trojan« to yield first result
389 }};
390 }
391 };
392
393
394
395} // namespace lib
396#endif /*LIB_LAZY_INIT_H*/
Buffer to place and maintain an object instance privately within another object.
Mix-in for lazy/delayed initialisation of an embedded functor.
LazyInit & operator=(LazyInit const &ref)
DelegateType< SIG > emptyInitialiser()
static DelegateType< SIG > * getPointerToDelegate(HeapStorage &buffer)
std::shared_ptr< HeapStorage > PendingInit
PendingInit && __trapLocked(PendingInit &&init)
DelegateType< void(void)> PlaceholderType
void installInitialiser(std::function< SIG > &targetFunctor, INI &&initialiser)
bool isInit() const
PendingInit const & __trapLocked(PendingInit const &init)
DelegateType< SIG > buildInitialiserDelegate(std::function< SIG > &targetFunctor, INI &&initialiser)
static std::function< SIG > maybeInvoke(PendingInit const &pendingInit, RawAddr location)
std::function< std::function< SIG > &(RawAddr)> DelegateType
PendingInit prepareInitialiser(std::function< SIG > &targetFunctor, INI &&initialiser)
LazyInit(std::function< SIG > &targetFunctor, INI &&initialiser, ARGS &&...parentCtorArgs)
prepare an initialiser to be activated on first use
LazyInit(MarkDisabled, ARGS &&...parentCtorArgs)
InPlaceBuffer< PlaceholderType > HeapStorage
LazyInit(LazyInit const &ref)
void installEmptyInitialiser()
PendingInit pendingInit_
manage heap storage for a pending initialisation closure
LazyInit(LazyInit &&rref)
»Trojan Function« builder.
static auto generateTrap(DEL *delegate)
Invocation: build a Lambda to activate the »Trap« and then to forward the invocation to the actual fu...
static auto buildTrapActivator(DEL *delegate, _Fun< RET(ARGS...)>)
Derived specific exceptions within Lumiera's exception hierarchy.
Definition error.hpp:193
Lumiera error handling (C interface).
Metaprogramming tools for detecting and transforming function types.
ptrdiff_t captureRawAddrOffset(RawAddr anchor, RawAddr subject)
Definition lazy-init.hpp:98
static TAR * relocate(RawAddr anchor, ptrdiff_t offset)
_DetectSingleArgFunction< FUN >::Arg _FunArg
abbreviation for referring to a function's single Argument type
Definition function.hpp:269
Implementation namespace for support and library code.
void const * RawAddr
Definition lazy-init.hpp:92
LumieraError< LERR_(STATE)> State
Definition error.hpp:209
bool isCloseBy(A &&a, B &&b, size_t consideredNearby=50)
determine heuristically if two objects are located „close to each other“ in memory.
Definition util.hpp:434
bool isSameObject(A const &a, B const &b)
compare plain object identity, based directly on the referee's memory identities.
Definition util.hpp:421
OBJ * unConst(const OBJ *)
shortcut to save some typing when having to define const and non-const variants of member functions
Definition util.hpp:358
Helper allowing type erasure while holding the actual object inline.
Trait template for uniform access to function signature types.
Definition function.hpp:144
Meta-function to check that some function like entity offers the expected signature.
Definition function.hpp:303
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...