Overte C++ Documentation
DependencyManager.h
1 //
2 // DependencyManager.h
3 //
4 //
5 // Created by ClĂ©ment Brisset on 12/10/14.
6 // Copyright 2014 High Fidelity, Inc.
7 //
8 // Distributed under the Apache License, Version 2.0.
9 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10 //
11 
12 #ifndef hifi_DependencyManager_h
13 #define hifi_DependencyManager_h
14 
15 #include <QtGlobal>
16 #include <QDebug>
17 #include <QHash>
18 #include <QtCore/QSharedPointer>
19 #include <QWeakPointer>
20 #include <QMutex>
21 
22 #include <functional>
23 #include <typeinfo>
24 
25 #define SINGLETON_DEPENDENCY \
26  friend class ::DependencyManager;
27 
28 class Dependency {
29 public:
30  typedef std::function<void(Dependency* pointer)> DeleterFunction;
31 
32 protected:
33  virtual ~Dependency() {}
34  virtual void customDeleter() {
35  _customDeleter(this);
36  }
37 
38  void setCustomDeleter(DeleterFunction customDeleter) { _customDeleter = customDeleter; }
39  DeleterFunction _customDeleter = [](Dependency* pointer) { delete pointer; };
40 
41  friend class DependencyManager;
42 };
43 
44 // usage:
45 // auto instance = DependencyManager::get<T>();
46 // auto instance = DependencyManager::set<T>(Args... args);
47 // DependencyManager::destroy<T>();
48 // DependencyManager::registerInheritance<Base, Derived>();
49 class DependencyManager {
50 public:
51  template<typename T>
52  static QSharedPointer<T> get();
53 
54  template<typename T>
55  static bool isSet();
56 
57  template<typename T, typename ...Args>
58  static QSharedPointer<T> set(Args&&... args);
59 
60  template<typename T, typename I, typename ...Args>
61  static QSharedPointer<T> set(Args&&... args);
62 
63  template<typename T>
64  static void destroy();
65 
66  template<typename Base, typename Derived>
67  static void registerInheritance();
68 
69  template <typename T>
70  static size_t typeHash() {
71 #ifdef Q_OS_ANDROID
72  size_t hashCode = std::hash<std::string>{}( typeid(T).name() );
73 #else
74  size_t hashCode = typeid(T).hash_code();
75 #endif
76  return hashCode;
77  }
78 
79  static void prepareToExit() { manager()._exiting = true; }
80 
81 private:
82  static DependencyManager& manager();
83 
84  template<typename T>
85  size_t getHashCode() const;
86 
87  QSharedPointer<Dependency> safeGet(size_t hashCode) const;
88 
89  QHash<size_t, QSharedPointer<Dependency>> _instanceHash;
90  QHash<size_t, size_t> _inheritanceHash;
91 
92  mutable QRecursiveMutex _instanceHashMutex;
93  mutable QMutex _inheritanceHashMutex;
94 
95  bool _exiting { false };
96 };
97 
98 template <typename T>
99 QSharedPointer<T> DependencyManager::get() {
100  static size_t hashCode = manager().getHashCode<T>();
101  static QWeakPointer<T> instance;
102 
103  if (instance.isNull()) {
104  instance = qSharedPointerCast<T>(manager().safeGet(hashCode));
105 
106 #ifndef QT_NO_DEBUG
107  // debug builds...
108  if (instance.isNull()) {
109  qWarning() << "DependencyManager::get(): No instance available for" << typeid(T).name();
110  }
111 #else
112  // for non-debug builds, don't print "No instance available" during shutdown, because
113  // the act of printing this often causes crashes (because the LogHandler has-been/is-being
114  // deleted).
115  if (!manager()._exiting && instance.isNull()) {
116  qWarning() << "DependencyManager::get(): No instance available for" << typeid(T).name();
117  }
118 #endif
119  }
120 
121  return instance.toStrongRef();
122 }
123 
124 template <typename T>
125 bool DependencyManager::isSet() {
126  static size_t hashCode = manager().getHashCode<T>();
127 
128  QSharedPointer<Dependency> instance = manager().safeGet(hashCode);
129  return !instance.isNull();
130 }
131 
132 template <typename T, typename ...Args>
133 QSharedPointer<T> DependencyManager::set(Args&&... args) {
134  static size_t hashCode = manager().getHashCode<T>();
135  QMutexLocker lock(&manager()._instanceHashMutex);
136 
137  // clear the previous instance before constructing the new instance
138  auto iter = manager()._instanceHash.find(hashCode);
139  if (iter != manager()._instanceHash.end()) {
140  iter.value().clear();
141  }
142 
143  QSharedPointer<T> newInstance(new T(args...), &T::customDeleter);
144  manager()._instanceHash.insert(hashCode, newInstance);
145 
146  return newInstance;
147 }
148 
149 template <typename T, typename I, typename ...Args>
150 QSharedPointer<T> DependencyManager::set(Args&&... args) {
151  static size_t hashCode = manager().getHashCode<T>();
152  QMutexLocker lock(&manager()._instanceHashMutex);
153 
154  // clear the previous instance before constructing the new instance
155  auto iter = manager()._instanceHash.find(hashCode);
156  if (iter != manager()._instanceHash.end()) {
157  iter.value().clear();
158  }
159 
160  QSharedPointer<T> newInstance(new I(args...), &I::customDeleter);
161  manager()._instanceHash.insert(hashCode, newInstance);
162 
163  return newInstance;
164 }
165 
166 template <typename T>
167 void DependencyManager::destroy() {
168  static size_t hashCode = manager().getHashCode<T>();
169 
170  QMutexLocker lock(&manager()._instanceHashMutex);
171  QSharedPointer<Dependency> shared = manager()._instanceHash.take(hashCode);
172  QWeakPointer<Dependency> weak = shared;
173  shared.clear();
174 
175  // Check that the dependency was actually destroyed. If it wasn't, it was improperly captured somewhere
176  if (weak.lock()) {
177  qWarning() << "DependencyManager::destroy():" << typeid(T).name() << "was not properly destroyed!";
178  }
179 }
180 
181 template<typename Base, typename Derived>
182 void DependencyManager::registerInheritance() {
183  size_t baseHashCode = typeHash<Base>();
184  size_t derivedHashCode = typeHash<Derived>();
185  QMutexLocker lock(&manager()._inheritanceHashMutex);
186  manager()._inheritanceHash.insert(baseHashCode, derivedHashCode);
187 }
188 
189 template<typename T>
190 size_t DependencyManager::getHashCode() const {
191  size_t hashCode = typeHash<T>();
192  QMutexLocker lock(&_inheritanceHashMutex);
193  auto derivedHashCode = _inheritanceHash.find(hashCode);
194 
195  while (derivedHashCode != _inheritanceHash.end()) {
196  hashCode = derivedHashCode.value();
197  derivedHashCode = _inheritanceHash.find(hashCode);
198  }
199 
200  return hashCode;
201 }
202 
203 #endif // hifi_DependencyManager_h