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) Lumiera.org
5  2023, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 */
22 
76 #ifndef LIB_LAZY_INIT_H
77 #define LIB_LAZY_INIT_H
78 
79 
80 #include "lib/error.h"
81 #include "lib/meta/function.hpp"
82 #include "lib/opaque-holder.hpp"
83 #include "lib/util.hpp"
84 
85 #include <functional>
86 #include <utility>
87 #include <memory>
88 
89 
90 namespace lib {
91  namespace err = lumiera::error;
92 
93  using lib::meta::_Fun;
94  using lib::meta::_FunArg;
95  using lib::meta::has_Sig;
96  using util::unConst;
97  using std::function;
98  using std::forward;
99  using std::move;
100 
101  using RawAddr = void const*;
102 
103 
104  namespace {// the anonymous namespace of horrors...
105 
106  inline ptrdiff_t
107  captureRawAddrOffset (RawAddr anchor, RawAddr subject)
108  {
109  // Dear Mr.Compiler, please get out of my way.
110  // I just genuinely want to shoot myself into my foot...
111  char* anchorAddr = reinterpret_cast<char*> (unConst(anchor));
112  char* subjectAddr = reinterpret_cast<char*> (unConst(subject));
113  return subjectAddr - anchorAddr;
114  }
115 
116  template<class TAR>
117  inline static TAR*
118  relocate (RawAddr anchor, ptrdiff_t offset)
119  {
120  char* anchorAddr = reinterpret_cast<char*> (unConst(anchor));
121  char* adjusted = anchorAddr + offset;
122  void* rawTarget = reinterpret_cast<void*> (adjusted);
123  return static_cast<TAR*> (rawTarget);
124  }
125 
126 
135  const ptrdiff_t FUNCTOR_PAYLOAD_OFFSET =
136  []{
137  size_t slot{42};
138  std::function<RawAddr(void)> probe = [slot]{ return RawAddr(&slot); };
139  RawAddr functor = &probe;
140  RawAddr payload = probe();
141  if (not util::isCloseBy(functor, payload))
142  throw err::Fatal{"Unable to use lib::LazyInit because std::function does not "
143  "apply small-object optimisation with inline storage."};
144  return captureRawAddrOffset (functor,payload);
145  }();
146  //
147  }//(End)low-level manipulations
148 
149 
150 
151 
157  template<class SIG>
158  class TrojanFun
159  {
160  template<class DEL, typename RET, typename... ARGS>
161  static auto
162  buildTrapActivator (DEL* delegate, _Fun<RET(ARGS...)>)
163  {
164  return [delegate]
165  (ARGS ...args) -> RET
166  {
167  auto currLocation = &delegate;
168  auto& functor = (*delegate) (currLocation);
169  //
170  return functor (forward<ARGS> (args)...);
171  };
172  }
173  public:
174 
186  template<class DEL>
187  static auto
188  generateTrap (DEL* delegate)
189  {
190  static_assert (_Fun<DEL>(), "Delegate must be function-like");
191  using Ret = typename _Fun<DEL>::Ret;
192  static_assert (_Fun<Ret>(), "Result from invoking delegate must also be function-like");
193  static_assert (has_Sig<Ret, SIG>(), "Result from delegate must expose target signature");
194 
195  REQUIRE (delegate);
196 
197  return buildTrapActivator (delegate, _Fun<SIG>());
198  }
199  };
200 
201 
202 
203 
204 
205  struct EmptyBase { };
206 
207  /**************************************************************/
214  template<class PAR =EmptyBase>
215  class LazyInit
216  : public PAR
217  {
218  template<class SIG>
219  using DelegateType = std::function<std::function<SIG>&(RawAddr)>;
220 
221  using PlaceholderType = DelegateType<void(void)>;
224 
227 
228 
229  PendingInit const&
230  __trapLocked (PendingInit const& init)
231  {
232  if (not init)
233  throw err::State{"Component was already configured with a processing function, "
234  "which binds into a fixed object location. It can not be moved anymore."
235  , err::LUMIERA_ERROR_LIFECYCLE};
236  return init;
237  }
238 
239  PendingInit&&
240  __trapLocked (PendingInit && init)
241  {
242  if (not init)
243  throw err::State{"Component was already configured with a processing function, "
244  "which binds into a fixed object location. It can not be moved anymore."
245  , err::LUMIERA_ERROR_LIFECYCLE};
246  return move (init);
247  }
248 
249 
250 
251  protected:
252  struct MarkDisabled{};
253 
255  template<typename...ARGS>
256  LazyInit (MarkDisabled, ARGS&& ...parentCtorArgs)
257  : PAR(forward<ARGS> (parentCtorArgs)...)
258  , pendingInit_{}
259  { }
260 
261 
262  public:
264  template<class SIG, class INI, typename...ARGS>
265  LazyInit (std::function<SIG>& targetFunctor, INI&& initialiser, ARGS&& ...parentCtorArgs)
266  : PAR(forward<ARGS> (parentCtorArgs)...)
267  , pendingInit_{}
268  {
269  installInitialiser (targetFunctor, forward<INI> (initialiser));
270  }
271 
272 
273  LazyInit (LazyInit const& ref)
274  : PAR{ref}
275  , pendingInit_{__trapLocked (ref.pendingInit_)}
276  { }
277 
278  LazyInit (LazyInit && rref)
279  : PAR{move (rref)}
280  , pendingInit_{__trapLocked (move(rref.pendingInit_))}
281  { }
282 
283  LazyInit&
284  operator= (LazyInit const& ref)
285  {
286  if (not util::isSameObject (ref, *this))
287  {
288  PAR::operator= (ref);
289  pendingInit_ = __trapLocked (ref.pendingInit_);
290  }
291  return *this;
292  }
293 
294  LazyInit&
295  operator= (LazyInit && rref)
296  {
297  if (not util::isSameObject (rref, *this))
298  {
299  PAR::operator= (move (rref));
300  pendingInit_ = __trapLocked (move (rref.pendingInit_));
301  }
302  return *this;
303  }
304 
305 
306  bool
307  isInit() const
308  {
309  return not pendingInit_;
310  }
311 
312  template<class SIG>
313  void
314  installEmptyInitialiser()
315  {
316  pendingInit_.reset (new HeapStorage{emptyInitialiser<SIG>()});
317  }
318 
319  template<class SIG, class INI>
320  void
321  installInitialiser (std::function<SIG>& targetFunctor, INI&& initialiser)
322  {
323  pendingInit_ = prepareInitialiser (targetFunctor, forward<INI> (initialiser));
324  }
325 
326 
327  private: /* ========== setup of the initialisation mechanism ========== */
328  template<class SIG>
329  DelegateType<SIG>
330  emptyInitialiser()
331  {
332  using TargetFun = std::function<SIG>;
333  return DelegateType<SIG>([disabledFunctor = TargetFun()]
334  (RawAddr) -> TargetFun&
335  {
336  return disabledFunctor;
337  });
338  }
339 
340  template<class SIG, class INI>
342  prepareInitialiser (std::function<SIG>& targetFunctor, INI&& initialiser)
343  {
344  if (isInit() and targetFunctor)
345  {// object is already »engaged« — no need to delay init
346  using ExpectedArg = _FunArg<INI>;
347  initialiser (static_cast<ExpectedArg> (this));
348  return PendingInit(); // keep engaged; no pending init
349  }
350  // else: prepare delayed init...
351  PendingInit storageHandle{
352  new HeapStorage{
353  buildInitialiserDelegate (targetFunctor, forward<INI> (initialiser))}};
354  // place a »Trojan« into the target functor to trigger initialisation on invocation...
355  targetFunctor = TrojanFun<SIG>::generateTrap (getPointerToDelegate<SIG> (*storageHandle));
356  return storageHandle;
357  }
358 
359  template<class SIG>
360  static DelegateType<SIG>*
361  getPointerToDelegate (HeapStorage& buffer)
362  {
363  return reinterpret_cast<DelegateType<SIG>*> (&buffer);
364  }
365 
366  template<class SIG>
367  static std::function<SIG>
368  maybeInvoke (PendingInit const& pendingInit, RawAddr location)
369  {
370  if (not pendingInit) // no pending init -> empty TargetFun
371  return std::function<SIG>();
372  auto* pendingDelegate = getPointerToDelegate<SIG>(*pendingInit);
373  return (*pendingDelegate) (location); // invoke to create new TargetFun
374  }
375 
376  template<class SIG, class INI>
377  DelegateType<SIG>
378  buildInitialiserDelegate (std::function<SIG>& targetFunctor, INI&& initialiser)
379  {
380  using TargetFun = std::function<SIG>;
381  using ExpectedArg = _FunArg<INI>;
382  return DelegateType<SIG>{
383  [performInit = forward<INI> (initialiser)
384  ,previousInit = move (pendingInit_)
385  ,targetOffset = captureRawAddrOffset (this, &targetFunctor)]
386  (RawAddr location) -> TargetFun&
387  {// apply known offset backwards to find current location of the host object
388  TargetFun* target = relocate<TargetFun> (location, -FUNCTOR_PAYLOAD_OFFSET);
389  LazyInit* self = relocate<LazyInit> (target, -targetOffset);
390  REQUIRE (self);
391  // tie storage to this (possibly recursive) call
392  auto storageHandle = move(self->pendingInit_);
393  // setup target as it would be with eager init
394  (*target) = maybeInvoke<SIG> (previousInit, location);
395  // invoke init, possibly downcast to derived *self
396  performInit (static_cast<ExpectedArg> (self));
397  return *target; // back to the »Trojan« to yield first result
398  }};
399  }
400  };
401 
402 
403 
404 } // namespace lib
405 #endif /*LIB_LAZY_INIT_H*/
Meta-function to check that some function like entity offers the expected signature.
Definition: function.hpp:241
»Trojan Function« builder.
Definition: lazy-init.hpp:158
LazyInit(std::function< SIG > &targetFunctor, INI &&initialiser, ARGS &&...parentCtorArgs)
prepare an initialiser to be activated on first use
Definition: lazy-init.hpp:265
Mix-in for lazy/delayed initialisation of an embedded functor.
Definition: lazy-init.hpp:215
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:188
Helper for uniform access to function signature types.
Definition: function.hpp:108
Lumiera error handling (C interface).
Implementation namespace for support and library code.
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:199
Metaprogramming tools for transforming functor types.
LazyInit(MarkDisabled, ARGS &&...parentCtorArgs)
Definition: lazy-init.hpp:256
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:212
PendingInit pendingInit_
manage heap storage for a pending initialisation closure
Definition: lazy-init.hpp:226
Buffer to place and maintain an object instance privately within another object.