DependencyInjection.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include "stormancer/BuildConfig.h"
4 #include "stormancer/Utilities/TypeReflection.h"
5 #include "stormancer/StormancerTypes.h"
6 #include "stormancer/Exceptions.h"
7 #include <memory>
8 #include <functional>
9 #include <unordered_map>
10 #include <vector>
11 #include <algorithm>
12 #include <stdexcept>
13 
17 
18 namespace Stormancer
19 {
20  class DependencyScope;
21  class ContainerBuilder;
22  class DependencyScopeImpl;
23 
24  enum class DependencyLifetime
25  {
26  InstancePerRequest,
27  InstancePerScope,
28  InstancePerMatchingScope,
29  SingleInstance
30  };
31 
32  using RegistrationId = uint64;
33 
35  {
36  std::function<std::shared_ptr<void>(const DependencyScope&)> factory;
37  RegistrationId id;
38  std::vector<std::pair<uint64, std::string>> typesRegisteredAs;
39  DependencyLifetime lifetime = DependencyLifetime::InstancePerRequest;
40  std::string scopeTagToMatch;
41  uint64 actualType;
42  };
43 
56  template<typename T>
58  {
59  public:
71  template<typename TAs>
73  {
74  return namedInternal<TAs>("");
75  }
76 
85  {
86  return as<T>();
87  }
88 
110  template<typename TAs>
111  RegistrationHandle& named(std::string name)
112  {
113  if (name.empty())
114  {
115  throw std::invalid_argument("name cannot be empty");
116  }
117  return namedInternal<TAs>(std::move(name));
118  }
119 
127  RegistrationHandle& instancePerRequest() { _data.lifetime = DependencyLifetime::InstancePerRequest; return *this; }
128 
137  RegistrationHandle& instancePerScope() { _data.lifetime = DependencyLifetime::InstancePerScope; return *this; }
138 
148  RegistrationHandle& singleInstance() { _data.lifetime = DependencyLifetime::SingleInstance; return *this; }
149 
162  {
163  if (scopeTag.empty())
164  {
165  throw std::invalid_argument("scopeTag must not be empty");
166  }
167  _data.lifetime = DependencyLifetime::InstancePerMatchingScope;
168  _data.scopeTagToMatch = scopeTag;
169  return *this;
170  }
171 
172  private:
173 
174  template<typename TAs>
175  RegistrationHandle& namedInternal(std::string name)
176  {
177  static_assert(std::is_convertible<T*, TAs*>::value, "You tried to register a dependency as a type it cannot be converted to.");
178 
179  auto namePair = std::make_pair(getTypeHash<TAs>(), std::move(name));
180  if (std::find(_data.typesRegisteredAs.begin(), _data.typesRegisteredAs.end(), namePair) == _data.typesRegisteredAs.end())
181  {
182  _data.typesRegisteredAs.push_back(std::move(namePair));
183  }
184 
185  return *this;
186  }
187 
188  RegistrationHandle(RegistrationData& data)
189  : _data(data)
190  {}
191 
192  friend class ContainerBuilder;
193 
194  RegistrationData& _data;
195  };
196 
197 
217  {
218  public:
219 
226  DependencyScope() = default;
227 
228  // Deleted to prevent the possibility of creating circular dependencies.
229  // If you want to copy the scope, what you actually want is most likely to create a child scope instead.
230  DependencyScope(const DependencyScope&) = delete;
231  DependencyScope& operator=(const DependencyScope&) = delete;
232 
234  DependencyScope& operator=(DependencyScope&&);
235 
248  template<typename T>
249  std::shared_ptr<T> resolve() const
250  {
251  uint64 typeHash = getTypeHash<T>();
252  auto instance = resolveInternal(typeHash, "");
253  return std::static_pointer_cast<T>(instance);
254  }
255 
267  template<typename T>
268  std::vector<std::shared_ptr<T>> resolveAll() const
269  {
270  uint64 typeHash = getTypeHash<T>();
271  auto instances = resolveAllInternal(typeHash);
272 
273  std::vector<std::shared_ptr<T>> results;
274  results.reserve(instances.size());
275  std::transform(instances.begin(), instances.end(), std::back_inserter(results), [](const std::shared_ptr<void>& ptr) { return std::static_pointer_cast<T>(ptr); });
276  return results;
277  }
278 
292  template<typename T>
293  std::shared_ptr<T> resolveNamed(const std::string& name) const
294  {
295  if (name.empty())
296  {
297  throw std::invalid_argument("name cannot be empty");
298  }
299 
300  uint64 typeHash = getTypeHash<T>();
301  auto instance = resolveInternal(typeHash, name);
302  return std::static_pointer_cast<T>(instance);
303  }
304 
310  {
311  return beginLifetimeScope("", std::function<void(ContainerBuilder&)>{});
312  }
313 
319  DependencyScope beginLifetimeScope(std::function<void(ContainerBuilder&)> builder) const
320  {
321  return beginLifetimeScope("", builder);
322  }
323 
329  DependencyScope beginLifetimeScope(std::string tag) const
330  {
331  return beginLifetimeScope(tag, std::function<void(ContainerBuilder&)>{});
332  }
333 
340  DependencyScope beginLifetimeScope(std::string tag, std::function<void(ContainerBuilder&)> builder) const;
341 
349  bool isValid() const;
350 
351  private:
352 
353  friend class DependencyScopeImpl;
354  friend class ContainerBuilder;
355 
356  DependencyScope(const ContainerBuilder& builder, std::string tag, std::shared_ptr<DependencyScopeImpl> parent);
357 
358  void throwIfNotValid() const;
359 
360  std::shared_ptr<void> resolveInternal(uint64 typeHash, const std::string& name) const;
361  std::vector<std::shared_ptr<void>> resolveAllInternal(uint64 typeHash) const;
362 
363  std::shared_ptr<DependencyScopeImpl> _impl;
364  };
365 
366 
383  {
384  public:
385 
387 
406  template<typename T>
407  RegistrationHandle<T> registerDependency(std::function<std::shared_ptr<T>(const DependencyScope&)> factory)
408  {
409  _registrations.emplace_back();
410  RegistrationData& data = _registrations.back();
411  data.factory = factory;
412  data.id = _registrationCounter;
413  data.actualType = getTypeHash<T>();
414  ++_registrationCounter;
415  return RegistrationHandle<T>(data);
416  }
417 
430  template<typename T>
431  RegistrationHandle<T> registerDependency(std::shared_ptr<T> instance)
432  {
433  return registerDependency<T>([instance](const DependencyScope&) { return instance; }).singleInstance();
434  }
435 
452  template<typename T, typename... TCtorArgs>
454 
459  template<typename T> struct All {};
460 
471 
472  private:
473 
474  friend class DependencyScopeImpl;
475 
476  ContainerBuilder(RegistrationId baseId) : _registrationCounter(baseId) {}
477 
478  template<typename T>
479  struct CtorResolver
480  {
481  static std::shared_ptr<T> resolve(const DependencyScope& scope)
482  {
483  return scope.resolve<T>();
484  }
485  };
486 
487  template<typename T>
488  struct CtorResolver<All<T>>
489  {
490  static std::vector<std::shared_ptr<T>> resolve(const DependencyScope& scope)
491  {
492  return scope.resolveAll<T>();
493  }
494  };
495 
496  RegistrationId _registrationCounter;
497  std::vector<RegistrationData> _registrations;
498  };
499 
500  //-------------------------------------------------------------------------------
501  //-------------------------------------------------------------------------------
502  // Definition is out-of-class to avoid incomplete type issue with DependencyScope
503  template<typename T, typename... TCtorArgs>
505  {
506  return registerDependency<T>([](const DependencyScope& scope)
507  {
508  (void)scope; // Suppress unused parameter warning when TCtorArgs is empty
509  return std::make_shared<T>(CtorResolver<TCtorArgs>::resolve(scope)...);
510  });
511  }
512 }
DependencyScope()=default
Create an empty DependencyScope.
DependencyScope build()
Build a DependencyScope from this container.
Definition: DependencyInjection.h:34
DependencyScope beginLifetimeScope(std::function< void(ContainerBuilder &)> builder) const
Create a child dependency scope.
Definition: DependencyInjection.h:319
RegistrationHandle< T > registerDependency(std::shared_ptr< T > instance)
Register an existing instance of type T as a dependency.
Definition: DependencyInjection.h:431
RegistrationHandle & asSelf()
Register the dependency as its own concrete type.
Definition: DependencyInjection.h:84
DependencyScope beginLifetimeScope(std::string tag) const
Create a child dependency scope.
Definition: DependencyInjection.h:329
DependencyScope beginLifetimeScope() const
Create a child dependency scope.
Definition: DependencyInjection.h:309
RegistrationHandle & instancePerMatchingScope(std::string scopeTag)
Set this dependency to use an instance per matching dependency scope.
Definition: DependencyInjection.h:161
bool isValid() const
Check if this scope has been fully constructed.
std::shared_ptr< T > resolve() const
Retrieve the dependency that was registered for the type T.
Definition: DependencyInjection.h:249
RegistrationHandle & instancePerRequest()
Set this dependency to have transient instances.
Definition: DependencyInjection.h:127
std::shared_ptr< T > resolveNamed(const std::string &name) const
Retrieve the dependency named name that was registered for the type T.
Definition: DependencyInjection.h:293
RegistrationHandle & named(std::string name)
Register this dependency as a given type, with a given name.
Definition: DependencyInjection.h:111
RegistrationHandle< T > registerDependency()
Register a dependency of type T with an automatically generated factory.
Definition: DependencyInjection.h:504
std::vector< std::shared_ptr< T > > resolveAll() const
Retrieve all the dependencies that were registered for the type T.
Definition: DependencyInjection.h:268
A "type tag" struct to be used as a type argument to registerDependency() when denoting a dependency ...
Definition: DependencyInjection.h:459
RegistrationHandle & as()
Register this dependency as a given type.
Definition: DependencyInjection.h:72
An object from which dependencies can be retrieved.
Definition: DependencyInjection.h:216
RegistrationHandle & singleInstance()
Set this dependency to have only one instance.
Definition: DependencyInjection.h:148
RegistrationHandle & instancePerScope()
Set this dependency to have an instance per dependency scope.
Definition: DependencyInjection.h:137
The ContainerBuilder is the primary element of the dependency injection mechanism....
Definition: DependencyInjection.h:382
RegistrationHandle< T > registerDependency(std::function< std::shared_ptr< T >(const DependencyScope &)> factory)
Register a dependency into the container.
Definition: DependencyInjection.h:407
The RegistrationHandle allows configuring a dependency added via ContainerBuilder::registerDependency...
Definition: DependencyInjection.h:57