Lumiera  0.pre.03
»edit your freedom«
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 
81 namespace lib {
82  namespace err = lumiera::error;
83 
84  using lib::meta::_Fun;
85  using lib::meta::_FunArg;
86  using lib::meta::has_Sig;
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
98  captureRawAddrOffset (RawAddr anchor, RawAddr subject)
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>
149  class TrojanFun
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 = typename _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>
206  class LazyInit
207  : public PAR
208  {
209  template<class SIG>
210  using DelegateType = std::function<std::function<SIG>&(RawAddr)>;
211 
212  using PlaceholderType = DelegateType<void(void)>;
215 
218 
219 
220  PendingInit const&
221  __trapLocked (PendingInit const& init)
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 
230  PendingInit&&
231  __trapLocked (PendingInit && init)
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}
266  , pendingInit_{__trapLocked (ref.pendingInit_)}
267  { }
268 
269  LazyInit (LazyInit && rref)
270  : PAR{move (rref)}
271  , pendingInit_{__trapLocked (move(rref.pendingInit_))}
272  { }
273 
274  LazyInit&
275  operator= (LazyInit const& ref)
276  {
277  if (not util::isSameObject (ref, *this))
278  {
279  PAR::operator= (ref);
280  pendingInit_ = __trapLocked (ref.pendingInit_);
281  }
282  return *this;
283  }
284 
285  LazyInit&
286  operator= (LazyInit && rref)
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
305  installEmptyInitialiser()
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>
321  emptyInitialiser()
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>*
352  getPointerToDelegate (HeapStorage& buffer)
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*/
Meta-function to check that some function like entity offers the expected signature.
Definition: function.hpp:232
»Trojan Function« builder.
Definition: lazy-init.hpp:149
LazyInit(std::function< SIG > &targetFunctor, INI &&initialiser, ARGS &&...parentCtorArgs)
prepare an initialiser to be activated on first use
Definition: lazy-init.hpp:256
Mix-in for lazy/delayed initialisation of an embedded functor.
Definition: lazy-init.hpp:206
static auto generateTrap(DEL *delegate)
Invocation: build a Lambda to activate the »Trap« and then to forward the invocation to the actual fu...
Definition: lazy-init.hpp:179
Helper for uniform access to function signature types.
Definition: function.hpp:99
Lumiera error handling (C interface).
Implementation namespace for support and library code.
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:190
Metaprogramming tools for transforming functor types.
LazyInit(MarkDisabled, ARGS &&...parentCtorArgs)
Definition: lazy-init.hpp:247
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
Helper allowing type erasure while holding the actual object inline.
typename _DetectSingleArgFunction< FUN >::Arg _FunArg
abbreviation for referring to a function&#39;s single Argument type
Definition: function.hpp:203
PendingInit pendingInit_
manage heap storage for a pending initialisation closure
Definition: lazy-init.hpp:217
Buffer to place and maintain an object instance privately within another object.