Lumiera  0.pre.03
»edityourfreedom«
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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 
113 #ifndef LIB_DEPEND_INJECT_H
114 #define LIB_DEPEND_INJECT_H
115 
116 
117 
118 #include "lib/error.hpp"
119 #include "lib/nocopy.hpp"
120 #include "lib/depend.hpp"
121 #include "lib/meta/trait.hpp"
122 #include "lib/meta/function.hpp"
123 #include "lib/sync-classlock.hpp"
124 
125 #include <type_traits>
126 #include <utility>
127 #include <memory>
128 
129 
130 namespace lib {
131  namespace error = lumiera::error;
132 
133  using std::forward;
134  using std::move;
135 
136 
137 
138 
155  template<class SRV>
158  {
159  using Factory = typename Depend<SRV>::Factory;
160  using Lock = typename Depend<SRV>::Lock;
161 
162  public:
169  template<class SUB>
170  static void
172  {
173  __assert_compatible<SUB>();
174  installFactory<SUB>();
175  }
176 
185  template<class FUN>
186  static void
187  useSingleton (FUN&& ctor)
188  {
189  using Sub = typename SubclassFactoryType<FUN>::Subclass;
190  using Fun = typename SubclassFactoryType<FUN>::Functor;
191 
192  __assert_compatible<Sub>();
193  installFactory<Sub,Fun> (forward<FUN> (ctor));
194  }
195 
196 
209  template<class IMP =SRV>
212  {
213  std::unique_ptr<IMP> instance_;
214 
215  public:
216  template<typename...ARGS>
217  ServiceInstance(ARGS&& ...ctorArgs)
218  : instance_{new IMP(forward<ARGS> (ctorArgs)...)}
219  {
220  __assert_compatible<IMP>();
221  activateServiceAccess (*instance_);
222  }
223 
225 
228  : instance_{}
229  {
230  __assert_compatible<IMP>();
231  }
232 
234  {
235  shutdown();
236  }
237 
238  template<typename...ARGS>
239  IMP&
240  createInstance(ARGS&& ...ctorArgs)
241  {
242  instance_.reset (new IMP(forward<ARGS> (ctorArgs)...));
243  activateServiceAccess (*instance_);
244  return *instance_;
245  }
246 
247  void
248  shutdown() noexcept
249  {
250  if (instance_)
251  {
253  instance_.reset();
254  }
255  }
256 
257 
258  explicit
259  operator bool() const
260  {
261  return bool(instance_);
262  }
263 
264  IMP&
265  operator* () const
266  {
267  ENSURE (instance_);
268  return *instance_;
269  }
270 
271  IMP*
272  operator->() const
273  {
274  ENSURE (instance_);
275  return instance_.get();
276  }
277  };
278 
279 
288  template<class MOC>
289  class Local
291  {
292  std::unique_ptr<MOC> mock_;
293 
296 
297  public:
299  : Local([]{ return new MOC{}; })
300  { }
301 
302  template<class FUN>
303  explicit
304  Local (FUN&& buildInstance)
305  {
306  __assert_compatible<MOC>();
307  __assert_compatible<typename SubclassFactoryType<FUN>::Subclass>();
308 
309  temporarilyInstallAlternateFactory (origInstance_, origFactory_
310  ,[=]()
311  {
312  mock_.reset (buildInstance());
313  return mock_.get();
314  });
315  }
317  {
318  restoreOriginalFactory (origInstance_, move(origFactory_));
319  }
320 
321  explicit
322  operator bool() const
323  {
324  return bool(mock_);
325  }
326 
327  MOC&
328  operator* () const
329  {
330  ENSURE (mock_);
331  return *mock_;
332  }
333 
334  MOC*
335  operator-> () const
336  {
337  ENSURE (mock_);
338  return mock_.get();
339  }
340  };
341 
342 
343 
344  protected: /* ======= internal access-API for those configurations to manipulate Depend<SRV> ======= */
345  template<class IMP>
346  friend class ServiceInstance;
347  template<class MOC>
348  friend class Local;
349 
350 
351  template<class SUB>
352  static void
354  {
355  static_assert (meta::is_Subclass<SUB,SRV>()
356  ,"Installed implementation class must be compatible to the interface.");
357  }
358 
359  static void
361  {
363  throw error::Logic("Attempt to reconfigure dependency injection after the fact. "
364  "The previously installed factory (typically Singleton) was already used."
365  , error::LUMIERA_ERROR_LIFECYCLE);
366  }
367 
368  template<typename FUN>
370  {
371  static_assert (meta::_Fun<FUN>(),
372  "Need a Lambda or Function object to create a heap allocated instance");
373 
374  using Functor = typename meta::_Fun<FUN>::Functor; // suitable type to store for later invocation
375  using ResultVal = typename meta::_Fun<FUN>::Ret;
376  using Subclass = typename meta::Strip<ResultVal>::TypePlain;
377 
378  static_assert (std::is_pointer<ResultVal>::value,
379  "Function must yield a pointer to a heap allocated instance");
380  };
381 
382 
383  template<class SUB, typename FUN>
384  static void
385  installFactory (FUN&& ctor)
386  {
387  Lock guard;
388  if (std::is_same<SRV,SUB>())
389  {
391  Depend<SRV>::factory.defineCreatorAndManage (forward<FUN> (ctor));
392  }
393  else
394  {
396  Depend<SRV>::factory.defineCreator ([]{ return & Depend<SUB>{}(); });
397  DependInject<SUB>::useSingleton (forward<FUN> (ctor));
398  } // delegate actual instance creation to Depend<SUB>
399  }
400 
401  template<class SUB>
402  static void
404  {
405  if (not std::is_same<SRV,SUB>())
406  {
407  Lock guard;
409  Depend<SRV>::factory.defineCreator ([]{ return & Depend<SUB>{}(); });
410  }
411  // note: we do not install an actual factory; rather we use the default for SUB
412  }
413 
414 
415  template<typename FUN>
416  static void
417  temporarilyInstallAlternateFactory (SRV*& stashInstance, Factory& stashFac, FUN&& newFac)
418  {
419  Lock guard;
420  stashFac.transferDefinition (move (Depend<SRV>::factory));
421  stashInstance = Depend<SRV>::instance;
422  Depend<SRV>::factory.defineCreator (forward<FUN>(newFac));
423  Depend<SRV>::instance = nullptr;
424  }
425 
426  static void
427  restoreOriginalFactory (SRV*& stashInstance, Factory&& stashFac)
428  {
429  Lock guard;
430  Depend<SRV>::factory.transferDefinition (move (stashFac));
431  Depend<SRV>::instance = stashInstance;
432  }
433 
434  static void
435  activateServiceAccess (SRV& newInstance)
436  {
437  Lock guard;
439  throw error::Logic("Attempt to activate an external service implementation, "
440  "but another instance has already been dependency-injected."
441  , error::LUMIERA_ERROR_LIFECYCLE);
442  Depend<SRV>::instance = &newInstance;
443  Depend<SRV>::factory.disable();
444  }
445 
446  static void
448  {
449  Lock guard;
450  Depend<SRV>::instance = nullptr;
451  Depend<SRV>::factory.disable();
452  }
453  };
454 
455 
456 
457 } // namespace lib
458 #endif /*LIB_DEPEND_INJECT_H*/
typename Depend< SessionCommandService >::Factory Factory
std::unique_ptr< IMP > instance_
static void temporarilyInstallAlternateFactory(SRV *&stashInstance, Factory &stashFac, FUN &&newFac)
static void installFactory()
Configuration handle to expose a service implementation through the Depend front-end.
std::remove_cv< TypePointee >::type TypePlain
Definition: trait.hpp:225
Not meant to be instantiated in any way.
Definition: nocopy.hpp:108
std::unique_ptr< MOC > mock_
Types marked with this mix-in may be moved but not copied.
Definition: nocopy.hpp:58
static void installFactory(FUN &&ctor)
IMP & createInstance(ARGS &&...ctorArgs)
static void deactivateServiceAccess()
Helper for uniform access to function signature types.
Definition: function.hpp:108
static void useSingleton()
configure dependency-injection for type SRV to build a subclass singleton.
static void __assert_compatible()
Access point to singletons and other kinds of dependencies designated by type.
Definition: depend.hpp:275
Implementation namespace for support and library code.
static void restoreOriginalFactory(SRV *&stashInstance, Factory &&stashFac)
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.
typename Depend< SessionCommandService >::Lock Lock
A special implementation of lib::Sync, where the storage of the object monitor is associated directly...
LumieraError< LERR_(LOGIC)> Logic
Definition: error.hpp:212
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...
static void __ensure_pristine()
Helper to abstract creation and lifecycle of a dependency.
Definition: depend.hpp:120
ServiceInstance(StartMode)
create in deactivated state.
Local(FUN &&buildInstance)
friend class ServiceInstance
verify compliance to an interface by subtype check
Definition: trait.hpp:278
static void activateServiceAccess(SRV &newInstance)
ENSURE(r==&pq)
Configuration handle for temporarily shadowing a dependency by a test mock instance.
friend class Local