Lumiera  0.pre.03
»edityourfreedom«
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 =SRV>
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 
328  MOC&
330  {
331  Depend<SRV>{}.operator()();
332  ENSURE (mock_);
333  return *mock_;
334  }
335 
336  MOC&
337  operator* () const
338  {
339  REQUIRE (mock_);
340  return *mock_;
341  }
342 
343  MOC*
344  operator-> () const
345  {
346  REQUIRE (mock_);
347  return mock_.get();
348  }
349  };
350 
351 
352 
353  protected: /* ======= internal access-API for those configurations to manipulate Depend<SRV> ======= */
354  template<class IMP>
355  friend class ServiceInstance;
356  template<class MOC>
357  friend class Local;
358 
359 
360  template<class SUB>
361  static void
363  {
364  static_assert (meta::is_Subclass<SUB,SRV>()
365  ,"Installed implementation class must be compatible to the interface.");
366  }
367 
368  static void
370  {
372  throw error::Logic("Attempt to reconfigure dependency injection after the fact. "
373  "The previously installed factory (typically Singleton) was already used."
374  , error::LUMIERA_ERROR_LIFECYCLE);
375  }
376 
377  template<typename FUN>
379  {
380  static_assert (meta::_Fun<FUN>(),
381  "Need a Lambda or Function object to create a heap allocated instance");
382 
383  using Functor = typename meta::_Fun<FUN>::Functor; // suitable type to store for later invocation
384  using ResultVal = typename meta::_Fun<FUN>::Ret;
386 
387  static_assert (std::is_pointer<ResultVal>::value,
388  "Function must yield a pointer to a heap allocated instance");
389  };
390 
391 
392  template<class SUB, typename FUN>
393  static void
394  installFactory (FUN&& ctor)
395  {
396  Lock guard;
397  if (std::is_same<SRV,SUB>())
398  {
400  Depend<SRV>::factory().defineCreatorAndManage (forward<FUN> (ctor));
401  }
402  else
403  {
405  Depend<SRV>::factory().defineCreator ([]{ return & Depend<SUB>{}(); });
406  DependInject<SUB>::useSingleton (forward<FUN> (ctor));
407  } // delegate actual instance creation to Depend<SUB>
408  }
409 
410  template<class SUB>
411  static void
413  {
414  if (not std::is_same<SRV,SUB>())
415  {
416  Lock guard;
418  Depend<SRV>::factory().defineCreator ([]{ return & Depend<SUB>{}(); });
419  }
420  // note: we do not install an actual factory; rather we use the default for SUB
421  }
422 
423 
424  template<typename FUN>
425  static void
426  temporarilyInstallAlternateFactory (SRV*& stashInstance, Factory& stashFac, FUN&& newFac)
427  {
428  Lock guard;
429  stashFac.transferDefinition (move (Depend<SRV>::factory()));
430  stashInstance = Depend<SRV>::instance;
431  Depend<SRV>::factory().defineCreator (forward<FUN>(newFac));
432  Depend<SRV>::instance = nullptr;
433  }
434 
435  static void
436  restoreOriginalFactory (SRV*& stashInstance, Factory&& stashFac)
437  {
438  Lock guard;
439  Depend<SRV>::factory().transferDefinition (move (stashFac));
440  Depend<SRV>::instance = stashInstance;
441  }
442 
443  static void
444  activateServiceAccess (SRV& newInstance)
445  {
446  Lock guard;
448  throw error::Logic("Attempt to activate an external service implementation, "
449  "but another instance has already been dependency-injected."
450  , error::LUMIERA_ERROR_LIFECYCLE);
451  Depend<SRV>::instance = &newInstance;
453  }
454 
455  static void
457  {
458  Lock guard;
459  Depend<SRV>::instance = nullptr;
461  }
462  };
463 
464 
465 
466 } // namespace lib
467 #endif /*LIB_DEPEND_INJECT_H*/
typename Depend< UILocationSolver >::Factory Factory
std::unique_ptr< IMP > instance_
static void temporarilyInstallAlternateFactory(SRV *&stashInstance, Factory &stashFac, FUN &&newFac)
static void installFactory()
typename meta::_Fun< FUN >::Functor Functor
Configuration handle to expose a service implementation through the Depend<SRV> 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()
typename meta::Strip< ResultVal >::TypePlain Subclass
void defineCreatorAndManage(FUN &&ctor)
Definition: depend.hpp:169
static void useSingleton()
configure dependency-injection for type SRV to build a subclass singleton.
void transferDefinition(DependencyFactory &&source)
Definition: depend.hpp:207
static void __assert_compatible()
Access point to singletons and other kinds of dependencies designated by type.
Definition: depend.hpp:289
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< UILocationSolver >::Lock Lock
void defineCreator(FUN &&ctor)
Definition: depend.hpp:162
A special implementation of lib::Sync, where the storage of the object monitor is associated directly...
LumieraError< LERR_(LOGIC)> Logic
Definition: error.hpp:212
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...
static void __ensure_pristine()
Helper to abstract creation and lifecycle of a dependency.
Definition: depend.hpp:134
ServiceInstance(StartMode)
create in deactivated state.
Local(FUN &&buildInstance)
static Factory & factory()
Definition: depend.hpp:299
verify compliance to an interface by subtype check
Definition: trait.hpp:278
static void activateServiceAccess(SRV &newInstance)
ENSURE(r==&pq)
typename meta::_Fun< FUN >::Ret ResultVal
Configuration handle for temporarily shadowing a dependency by a test mock instance.
friend class Local