Lumiera  0.pre.03
»edit your freedom«
depend-inject.hpp
Go to the documentation of this file.
1 /*
2  DEPEND-INJECT.hpp - managing the lifecycle of singletons and dependencies
3 
4  Copyright (C)
5  2018, 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 
14 
105 #ifndef LIB_DEPEND_INJECT_H
106 #define LIB_DEPEND_INJECT_H
107 
108 
109 
110 #include "lib/error.hpp"
111 #include "lib/nocopy.hpp"
112 #include "lib/depend.hpp"
113 #include "lib/meta/trait.hpp"
114 #include "lib/meta/function.hpp"
115 #include "lib/sync-classlock.hpp"
116 
117 #include <type_traits>
118 #include <utility>
119 #include <memory>
120 
121 
122 namespace lib {
123  namespace error = lumiera::error;
124 
125  using std::forward;
126  using std::move;
127 
128 
129 
130 
147  template<class SRV>
150  {
151  using Factory = typename Depend<SRV>::Factory;
152  using Lock = typename Depend<SRV>::Lock;
153 
154  public:
161  template<class SUB>
162  static void
164  {
165  __assert_compatible<SUB>();
166  installFactory<SUB>();
167  }
168 
177  template<class FUN>
178  static void
179  useSingleton (FUN&& ctor)
180  {
181  using Sub = typename SubclassFactoryType<FUN>::Subclass;
182  using Fun = typename SubclassFactoryType<FUN>::Functor;
183 
184  __assert_compatible<Sub>();
185  installFactory<Sub,Fun> (forward<FUN> (ctor));
186  }
187 
188 
201  template<class IMP =SRV>
204  {
205  std::unique_ptr<IMP> instance_;
206 
207  public:
208  template<typename...ARGS>
209  ServiceInstance(ARGS&& ...ctorArgs)
210  : instance_{new IMP(forward<ARGS> (ctorArgs)...)}
211  {
212  __assert_compatible<IMP>();
213  activateServiceAccess (*instance_);
214  }
215 
216  enum StartMode { NOT_YET_STARTED };
217 
219  ServiceInstance(StartMode)
220  : instance_{}
221  {
222  __assert_compatible<IMP>();
223  }
224 
225  ~ServiceInstance()
226  {
227  shutdown();
228  }
229 
230  template<typename...ARGS>
231  IMP&
232  createInstance(ARGS&& ...ctorArgs)
233  {
234  instance_.reset (new IMP(forward<ARGS> (ctorArgs)...));
235  activateServiceAccess (*instance_);
236  return *instance_;
237  }
238 
239  void
240  shutdown() noexcept
241  {
242  if (instance_)
243  {
244  deactivateServiceAccess();
245  instance_.reset();
246  }
247  }
248 
249 
250  explicit
251  operator bool() const
252  {
253  return bool(instance_);
254  }
255 
256  IMP&
257  operator* () const
258  {
259  ENSURE (instance_);
260  return *instance_;
261  }
262 
263  IMP*
264  operator->() const
265  {
266  ENSURE (instance_);
267  return instance_.get();
268  }
269  };
270 
271 
280  template<class MOC =SRV>
281  class Local
283  {
284  std::unique_ptr<MOC> mock_;
285 
286  SRV* origInstance_;
287  Factory origFactory_;
288 
289  public:
290  Local()
291  : Local([]{ return new MOC{}; })
292  { }
293 
294  template<class FUN>
295  explicit
296  Local (FUN&& buildInstance)
297  {
298  __assert_compatible<MOC>();
299  __assert_compatible<typename SubclassFactoryType<FUN>::Subclass>();
300 
301  temporarilyInstallAlternateFactory (origInstance_, origFactory_
302  ,[=]()
303  {
304  mock_.reset (buildInstance());
305  return mock_.get();
306  });
307  }
308  ~Local()
309  {
310  restoreOriginalFactory (origInstance_, move(origFactory_));
311  }
312 
313  explicit
314  operator bool() const
315  {
316  return bool(mock_);
317  }
318 
320  MOC&
322  {
323  Depend<SRV>{}.operator()();
324  ENSURE (mock_);
325  return *mock_;
326  }
327 
328  MOC&
329  operator* () const
330  {
331  REQUIRE (mock_);
332  return *mock_;
333  }
334 
335  MOC*
336  operator-> () const
337  {
338  REQUIRE (mock_);
339  return mock_.get();
340  }
341  };
342 
343 
344 
345  protected: /* ======= internal access-API for those configurations to manipulate Depend<SRV> ======= */
346  template<class IMP>
347  friend class ServiceInstance;
348  template<class MOC>
349  friend class Local;
350 
351 
352  template<class SUB>
353  static void
354  __assert_compatible()
355  {
356  static_assert (meta::is_Subclass<SUB,SRV>()
357  ,"Installed implementation class must be compatible to the interface.");
358  }
359 
360  static void
361  __ensure_pristine()
362  {
364  throw error::Logic("Attempt to reconfigure dependency injection after the fact. "
365  "The previously installed factory (typically Singleton) was already used."
366  , error::LUMIERA_ERROR_LIFECYCLE);
367  }
368 
369  template<typename FUN>
371  {
372  static_assert (meta::_Fun<FUN>(),
373  "Need a Lambda or Function object to create a heap allocated instance");
374 
375  using Functor = typename meta::_Fun<FUN>::Functor; // suitable type to store for later invocation
376  using ResultVal = typename meta::_Fun<FUN>::Ret;
377  using Subclass = typename meta::Strip<ResultVal>::TypePlain;
378 
379  static_assert (std::is_pointer<ResultVal>::value,
380  "Function must yield a pointer to a heap allocated instance");
381  };
382 
383 
384  template<class SUB, typename FUN>
385  static void
386  installFactory (FUN&& ctor)
387  {
388  Lock guard;
389  if (std::is_same<SRV,SUB>())
390  {
391  __ensure_pristine();
392  Depend<SRV>::factory().defineCreatorAndManage (forward<FUN> (ctor));
393  }
394  else
395  {
396  __ensure_pristine();
397  Depend<SRV>::factory().defineCreator ([]{ return & Depend<SUB>{}(); });
398  DependInject<SUB>::useSingleton (forward<FUN> (ctor));
399  } // delegate actual instance creation to Depend<SUB>
400  }
401 
402  template<class SUB>
403  static void
404  installFactory ()
405  {
406  if (not std::is_same<SRV,SUB>())
407  {
408  Lock guard;
409  __ensure_pristine();
410  Depend<SRV>::factory().defineCreator ([]{ return & Depend<SUB>{}(); });
411  }
412  // note: we do not install an actual factory; rather we use the default for SUB
413  }
414 
415 
416  template<typename FUN>
417  static void
418  temporarilyInstallAlternateFactory (SRV*& stashInstance, Factory& stashFac, FUN&& newFac)
419  {
420  Lock guard;
421  stashFac.transferDefinition (move (Depend<SRV>::factory()));
422  stashInstance = Depend<SRV>::instance;
423  Depend<SRV>::factory().defineCreator (forward<FUN>(newFac));
424  Depend<SRV>::instance = nullptr;
425  }
426 
427  static void
428  restoreOriginalFactory (SRV*& stashInstance, Factory&& stashFac)
429  {
430  Lock guard;
431  Depend<SRV>::factory().transferDefinition (move (stashFac));
432  Depend<SRV>::instance = stashInstance;
433  }
434 
435  static void
436  activateServiceAccess (SRV& newInstance)
437  {
438  Lock guard;
440  throw error::Logic("Attempt to activate an external service implementation, "
441  "but another instance has already been dependency-injected."
442  , error::LUMIERA_ERROR_LIFECYCLE);
443  Depend<SRV>::instance = &newInstance;
444  Depend<SRV>::factory().disable();
445  }
446 
447  static void
448  deactivateServiceAccess()
449  {
450  Lock guard;
451  Depend<SRV>::instance = nullptr;
452  Depend<SRV>::factory().disable();
453  }
454  };
455 
456 
457 
458 } // namespace lib
459 #endif /*LIB_DEPEND_INJECT_H*/
Configuration handle to expose a service implementation through the Depend<SRV> front-end.
Not meant to be instantiated in any way.
Definition: nocopy.hpp:112
Types marked with this mix-in may be moved but not copied.
Definition: nocopy.hpp:49
static void useSingleton()
configure dependency-injection for type SRV to build a subclass singleton.
Access point to singletons and other kinds of dependencies designated by type.
Definition: depend.hpp:280
Implementation namespace for support and library code.
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:190
Mix-Ins to allow or prohibit various degrees of copying and cloning.
This framework allows to (re)configure the lib::Depend front-end for dependency-injection.
Metaprogramming tools for transforming functor types.
A special implementation of lib::Sync, where the storage of the object monitor is associated directly...
MOC & triggerCreate()
trigger lazy service object instantiation
Singleton services and Dependency Injection.
Helpers for type detection, type rewriting and metaprogramming.
static void useSingleton(FUN &&ctor)
configure dependency-injection for type SRV to manage a subclass singleton, which is created lazily o...
Lumiera error handling (C++ interface).
A synchronisation protection guard employing a lock scoped to the parameter type as a whole...
Helper to abstract creation and lifecycle of a dependency.
Definition: depend.hpp:125
ServiceInstance(StartMode)
create in deactivated state.
verify compliance to an interface by subtype check
Definition: trait.hpp:323
Configuration handle for temporarily shadowing a dependency by a test mock instance.