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) Lumiera.org
5  2018, 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 
23 
114 #ifndef LIB_DEPEND_INJECT_H
115 #define LIB_DEPEND_INJECT_H
116 
117 
118 
119 #include "lib/error.hpp"
120 #include "lib/nocopy.hpp"
121 #include "lib/depend.hpp"
122 #include "lib/meta/trait.hpp"
123 #include "lib/meta/function.hpp"
124 #include "lib/sync-classlock.hpp"
125 
126 #include <type_traits>
127 #include <utility>
128 #include <memory>
129 
130 
131 namespace lib {
132  namespace error = lumiera::error;
133 
134  using std::forward;
135  using std::move;
136 
137 
138 
139 
156  template<class SRV>
159  {
160  using Factory = typename Depend<SRV>::Factory;
161  using Lock = typename Depend<SRV>::Lock;
162 
163  public:
170  template<class SUB>
171  static void
173  {
174  __assert_compatible<SUB>();
175  installFactory<SUB>();
176  }
177 
186  template<class FUN>
187  static void
188  useSingleton (FUN&& ctor)
189  {
190  using Sub = typename SubclassFactoryType<FUN>::Subclass;
191  using Fun = typename SubclassFactoryType<FUN>::Functor;
192 
193  __assert_compatible<Sub>();
194  installFactory<Sub,Fun> (forward<FUN> (ctor));
195  }
196 
197 
210  template<class IMP =SRV>
213  {
214  std::unique_ptr<IMP> instance_;
215 
216  public:
217  template<typename...ARGS>
218  ServiceInstance(ARGS&& ...ctorArgs)
219  : instance_{new IMP(forward<ARGS> (ctorArgs)...)}
220  {
221  __assert_compatible<IMP>();
222  activateServiceAccess (*instance_);
223  }
224 
225  enum StartMode { NOT_YET_STARTED };
226 
228  ServiceInstance(StartMode)
229  : instance_{}
230  {
231  __assert_compatible<IMP>();
232  }
233 
234  ~ServiceInstance()
235  {
236  shutdown();
237  }
238 
239  template<typename...ARGS>
240  IMP&
241  createInstance(ARGS&& ...ctorArgs)
242  {
243  instance_.reset (new IMP(forward<ARGS> (ctorArgs)...));
244  activateServiceAccess (*instance_);
245  return *instance_;
246  }
247 
248  void
249  shutdown() noexcept
250  {
251  if (instance_)
252  {
253  deactivateServiceAccess();
254  instance_.reset();
255  }
256  }
257 
258 
259  explicit
260  operator bool() const
261  {
262  return bool(instance_);
263  }
264 
265  IMP&
266  operator* () const
267  {
268  ENSURE (instance_);
269  return *instance_;
270  }
271 
272  IMP*
273  operator->() const
274  {
275  ENSURE (instance_);
276  return instance_.get();
277  }
278  };
279 
280 
289  template<class MOC =SRV>
290  class Local
292  {
293  std::unique_ptr<MOC> mock_;
294 
295  SRV* origInstance_;
296  Factory origFactory_;
297 
298  public:
299  Local()
300  : Local([]{ return new MOC{}; })
301  { }
302 
303  template<class FUN>
304  explicit
305  Local (FUN&& buildInstance)
306  {
307  __assert_compatible<MOC>();
308  __assert_compatible<typename SubclassFactoryType<FUN>::Subclass>();
309 
310  temporarilyInstallAlternateFactory (origInstance_, origFactory_
311  ,[=]()
312  {
313  mock_.reset (buildInstance());
314  return mock_.get();
315  });
316  }
317  ~Local()
318  {
319  restoreOriginalFactory (origInstance_, move(origFactory_));
320  }
321 
322  explicit
323  operator bool() const
324  {
325  return bool(mock_);
326  }
327 
329  MOC&
331  {
332  Depend<SRV>{}.operator()();
333  ENSURE (mock_);
334  return *mock_;
335  }
336 
337  MOC&
338  operator* () const
339  {
340  REQUIRE (mock_);
341  return *mock_;
342  }
343 
344  MOC*
345  operator-> () const
346  {
347  REQUIRE (mock_);
348  return mock_.get();
349  }
350  };
351 
352 
353 
354  protected: /* ======= internal access-API for those configurations to manipulate Depend<SRV> ======= */
355  template<class IMP>
356  friend class ServiceInstance;
357  template<class MOC>
358  friend class Local;
359 
360 
361  template<class SUB>
362  static void
363  __assert_compatible()
364  {
365  static_assert (meta::is_Subclass<SUB,SRV>()
366  ,"Installed implementation class must be compatible to the interface.");
367  }
368 
369  static void
370  __ensure_pristine()
371  {
373  throw error::Logic("Attempt to reconfigure dependency injection after the fact. "
374  "The previously installed factory (typically Singleton) was already used."
375  , error::LUMIERA_ERROR_LIFECYCLE);
376  }
377 
378  template<typename FUN>
380  {
381  static_assert (meta::_Fun<FUN>(),
382  "Need a Lambda or Function object to create a heap allocated instance");
383 
384  using Functor = typename meta::_Fun<FUN>::Functor; // suitable type to store for later invocation
385  using ResultVal = typename meta::_Fun<FUN>::Ret;
386  using Subclass = typename meta::Strip<ResultVal>::TypePlain;
387 
388  static_assert (std::is_pointer<ResultVal>::value,
389  "Function must yield a pointer to a heap allocated instance");
390  };
391 
392 
393  template<class SUB, typename FUN>
394  static void
395  installFactory (FUN&& ctor)
396  {
397  Lock guard;
398  if (std::is_same<SRV,SUB>())
399  {
400  __ensure_pristine();
401  Depend<SRV>::factory().defineCreatorAndManage (forward<FUN> (ctor));
402  }
403  else
404  {
405  __ensure_pristine();
406  Depend<SRV>::factory().defineCreator ([]{ return & Depend<SUB>{}(); });
407  DependInject<SUB>::useSingleton (forward<FUN> (ctor));
408  } // delegate actual instance creation to Depend<SUB>
409  }
410 
411  template<class SUB>
412  static void
413  installFactory ()
414  {
415  if (not std::is_same<SRV,SUB>())
416  {
417  Lock guard;
418  __ensure_pristine();
419  Depend<SRV>::factory().defineCreator ([]{ return & Depend<SUB>{}(); });
420  }
421  // note: we do not install an actual factory; rather we use the default for SUB
422  }
423 
424 
425  template<typename FUN>
426  static void
427  temporarilyInstallAlternateFactory (SRV*& stashInstance, Factory& stashFac, FUN&& newFac)
428  {
429  Lock guard;
430  stashFac.transferDefinition (move (Depend<SRV>::factory()));
431  stashInstance = Depend<SRV>::instance;
432  Depend<SRV>::factory().defineCreator (forward<FUN>(newFac));
433  Depend<SRV>::instance = nullptr;
434  }
435 
436  static void
437  restoreOriginalFactory (SRV*& stashInstance, Factory&& stashFac)
438  {
439  Lock guard;
440  Depend<SRV>::factory().transferDefinition (move (stashFac));
441  Depend<SRV>::instance = stashInstance;
442  }
443 
444  static void
445  activateServiceAccess (SRV& newInstance)
446  {
447  Lock guard;
449  throw error::Logic("Attempt to activate an external service implementation, "
450  "but another instance has already been dependency-injected."
451  , error::LUMIERA_ERROR_LIFECYCLE);
452  Depend<SRV>::instance = &newInstance;
453  Depend<SRV>::factory().disable();
454  }
455 
456  static void
457  deactivateServiceAccess()
458  {
459  Lock guard;
460  Depend<SRV>::instance = nullptr;
461  Depend<SRV>::factory().disable();
462  }
463  };
464 
465 
466 
467 } // namespace lib
468 #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:108
Types marked with this mix-in may be moved but not copied.
Definition: nocopy.hpp:58
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:289
Implementation namespace for support and library code.
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:196
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:134
ServiceInstance(StartMode)
create in deactivated state.
verify compliance to an interface by subtype check
Definition: trait.hpp:316
Configuration handle for temporarily shadowing a dependency by a test mock instance.