AusweisApp2
Env.h
gehe zur Dokumentation dieser Datei
1 /*
2  * \brief Runtime environment to create (mockable) objects.
3  *
4  * \copyright Copyright (c) 2017-2021 Governikus GmbH & Co. KG, Germany
5  */
6 
7 #pragma once
8 
9 #include <functional>
10 #include <type_traits>
11 
12 #include <QCoreApplication>
13 #include <QDebug>
14 #include <QMap>
15 #include <QMetaObject>
16 #include <QMetaType>
17 #include <QObject>
18 #include <QObjectCleanupHandler>
19 #include <QPointer>
20 #include <QReadLocker>
21 #include <QReadWriteLock>
22 #include <QSharedPointer>
23 #include <QThread>
24 #include <QWeakPointer>
25 #include <QWriteLocker>
26 
27 #ifndef QT_NO_DEBUG
28 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
29 #include <QMutableVectorIterator>
30 #endif
31 
32 #include <QVector>
33 #endif
34 
35 class test_Env;
36 
37 namespace governikus
38 {
39 
40 template<typename T> T* singleton();
41 template<typename T, typename ... Args> T createNewObject(Args&& ... pArgs);
42 
43 class Env
44 {
45  public:
46  struct ThreadSafe {};
47 
48  private:
49  friend class ::test_Env;
50  Q_DISABLE_COPY(Env)
51  using Identifier = const char*;
52 
53 #ifndef QT_NO_DEBUG
54  class FuncWrapperBase
55  {
56  protected:
57  int mCounter = 0;
58 
59  public:
60  [[nodiscard]] inline int getCounter() const
61  {
62  return mCounter;
63  }
64 
65 
66  inline void reset()
67  {
68  mCounter = 0;
69  }
70 
71 
72  virtual ~FuncWrapperBase();
73  };
74 
75  template<typename T, typename ... Args>
76  class FuncWrapper final
77  : public FuncWrapperBase
78  {
79  private:
80  const std::function<T(Args ...)> mFunc;
81 
82  public:
83  explicit FuncWrapper(std::function<T(Args ...)> pFunc)
84  : mFunc(std::move(pFunc))
85  {
86  }
87 
88 
89  T operator()(Args&& ... pArgs)
90  {
91  ++mCounter;
92  return mFunc(pArgs ...);
93  }
94 
95 
96  };
97 
98  using Wrapper = QSharedPointer<FuncWrapperBase>;
99  QVector<Wrapper> mInstancesCreator;
100  QMap<Identifier, void*> mInstancesSingleton;
101  mutable QReadWriteLock mLock;
102 #endif
103 
104  QPointer<QObjectCleanupHandler> mSingletonHandler;
105  QVector<std::function<void* (bool)>> mSingletonCreator;
106 
107  QMap<Identifier, QWeakPointer<QObject>> mSharedInstances;
108  mutable QReadWriteLock mSharedInstancesLock;
109 
110  static Env& getInstance();
111 
112  template<typename T>
113  T* createSingleton()
114  {
115  Q_ASSERT(!mSingletonHandler.isNull());
116 #ifndef QT_NO_DEBUG
117  if (!QCoreApplication::startingUp() && !QCoreApplication::applicationName().startsWith(QLatin1String("Test_")))
118  {
119  Q_ASSERT(QThread::currentThread()->objectName() == QLatin1String("MainThread"));
120  }
121 #endif
122 
123  qDebug() << "Create singleton:" << T::staticMetaObject.className();
124 
125  T* ptr = nullptr;
126  if constexpr (std::is_abstract<T>::value && std::is_destructible<T>::value)
127  {
128  ptr = createNewObject<T*>();
129  }
130  else
131  {
132  ptr = new T();
133  }
134 
135  QObject::connect(ptr, &QObject::destroyed, ptr, [] {
136  qDebug() << "Destroy singleton:" << T::staticMetaObject.className();
137  });
138  mSingletonHandler->add(ptr);
139  mSingletonCreator << std::bind(&Env::getOrCreateSingleton<T>, this, std::placeholders::_1);
140 
141  return ptr;
142  }
143 
144 
145  template<typename T>
146  T* getOrCreateSingleton(bool pCreate = false)
147  {
148  static QPointer<T> instance = createSingleton<T>();
149 
150  if (Q_UNLIKELY(pCreate))
151  {
152  // It's not thread-safe! So Env::init() should be the only one!
153  Q_ASSERT(instance.isNull());
154  instance = createSingleton<T>();
155  }
156 
157  return instance;
158  }
159 
160 
161  template<typename T>
162  inline T* fetchRealSingleton()
163  {
164  if constexpr (QtPrivate::IsPointerToTypeDerivedFromQObject<T*>::Value)
165  {
166  return getOrCreateSingleton<T>();
167  }
168  else
169  {
170  if constexpr (std::is_abstract<T>::value && std::is_destructible<T>::value)
171  {
172  static_assert(std::has_virtual_destructor<T>::value, "Destructor must be virtual");
173  return singleton<T>();
174  }
175  else
176  {
177  return &T::getInstance();
178  }
179  }
180  }
181 
182 
183  template<typename T>
184  inline typename std::enable_if<QtPrivate::IsGadgetHelper<T>::IsRealGadget, T*>::type checkObjectInfo(Identifier pId, T* pObject) const
185  {
186  Q_UNUSED(pId)
187  return pObject;
188  }
189 
190 
191  template<typename T>
192  inline typename std::enable_if<QtPrivate::IsPointerToTypeDerivedFromQObject<T*>::Value, T*>::type checkObjectInfo(Identifier pId, T* pObject) const
193  {
194  if (!std::is_base_of<ThreadSafe, T>() && pObject->thread() != QThread::currentThread())
195  {
196  qWarning() << pId << "was created in" << pObject->thread()->objectName() << "but is requested by" << QThread::currentThread()->objectName();
197 #ifndef QT_NO_DEBUG
198  Q_ASSERT(QCoreApplication::applicationName().startsWith(QLatin1String("Test_global_Env")));
199 #endif
200  }
201 
202  return pObject;
203  }
204 
205 
206  template<typename T>
207  inline T* fetchSingleton()
208  {
209  static_assert(QtPrivate::IsGadgetHelper<T>::IsRealGadget || QtPrivate::IsPointerToTypeDerivedFromQObject<T*>::Value,
210  "Singletons needs to be a Q_GADGET or an QObject/Q_OBJECT");
211 
212  const Identifier id = T::staticMetaObject.className();
213  void* obj = nullptr;
214 #ifndef QT_NO_DEBUG
215  const QReadLocker locker(&mLock);
216  obj = mInstancesSingleton.value(id);
217  if (!obj)
218 #endif
219  obj = fetchRealSingleton<T>();
220  Q_ASSERT(obj);
221  return checkObjectInfo(id, static_cast<T*>(obj));
222  }
223 
224 
225  template<typename T, typename ... Args>
226  inline T newObject(Args&& ... pArgs) const
227  {
228  if constexpr (std::is_constructible<typename std::remove_pointer<T>::type, Args ...>::value)
229  {
230  if constexpr (std::is_pointer<T>::value)
231  {
232  using t = typename std::remove_pointer<T>::type;
233  return new t(std::forward<Args>(pArgs) ...);
234  }
235  else
236  {
237  return T(std::forward<Args>(pArgs) ...);
238  }
239  }
240  else
241  {
242  static_assert(std::is_pointer<T>::value, "It is impossible to return implementation of interface by value. Use pointer or add constructor!");
243  auto obj = createNewObject<T>(std::forward<Args>(pArgs) ...);
244  Q_ASSERT(obj);
245  return obj;
246  }
247  }
248 
249 
250  template<typename T, typename ... Args>
251  T createObject(Args&& ... pArgs) const
252  {
253 #ifndef QT_NO_DEBUG
254  {
255  QReadLocker locker(&mLock);
256 
257  // copy QSharedPointer "mock" to increase ref-counter. Otherwise
258  // unlock would allow to delete the wrapper.
259  for (auto mock : qAsConst(mInstancesCreator)) // clazy:exclude=range-loop
260  {
261  auto creator = mock.dynamicCast<FuncWrapper<T, Args ...>>();
262  if (creator)
263  {
264  locker.unlock();
265  return (*creator)(std::forward<Args>(pArgs) ...);
266  }
267  }
268  }
269 #endif
270 
271  return newObject<T>(std::forward<Args>(pArgs) ...);
272  }
273 
274 
275  void initialize()
276  {
277  Q_ASSERT(mSingletonHandler.isNull());
278  mSingletonHandler = new QObjectCleanupHandler();
279  QObject::connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, mSingletonHandler.data(), &QObject::deleteLater);
280 
281  const auto copy = mSingletonCreator;
282  mSingletonCreator.clear();
283  for (const auto& func : copy)
284  {
285  func(true);
286  }
287  }
288 
289  protected:
290  Env();
291  ~Env() = default;
292 
293  public:
294  static void init()
295  {
296  getInstance().initialize();
297  }
298 
299 
300  template<typename T>
301  static T* getSingleton()
302  {
303  return getInstance().fetchSingleton<T>();
304  }
305 
306 
307  template<typename T, typename ... Args>
308  static T create(Args&& ... pArgs)
309  {
310  return getInstance().createObject<T>(std::forward<Args>(pArgs) ...);
311  }
312 
313 
314  template<typename T>
315  static QSharedPointer<T> getShared()
316  {
317  static_assert(QtPrivate::IsGadgetHelper<T>::IsRealGadget || QtPrivate::IsPointerToTypeDerivedFromQObject<T*>::Value,
318  "Shared class needs to be a Q_GADGET or an QObject/Q_OBJECT");
319 
320  const Identifier className = T::staticMetaObject.className();
321 
322  auto& holder = getInstance();
323  holder.mSharedInstancesLock.lockForRead();
324  QSharedPointer<T> shared = qSharedPointerCast<T>(holder.mSharedInstances.value(className));
325  holder.mSharedInstancesLock.unlock();
326 
327  if (!shared)
328  {
329  const QWriteLocker locker(&holder.mSharedInstancesLock);
330  shared = qSharedPointerCast<T>(holder.mSharedInstances.value(className));
331  if (!shared)
332  {
333  qDebug() << "Spawn shared instance:" << className;
334  shared = QSharedPointer<T>::create();
335  holder.mSharedInstances.insert(className, shared.toWeakRef());
336  }
337  }
338 
339  return shared;
340  }
341 
342 
343 #ifndef QT_NO_DEBUG
344  static void resetCounter();
345  static void clear();
346  static void set(const QMetaObject& pMetaObject, void* pObject = nullptr);
347 
348  template<typename T, typename ... Args>
349  static int getCounter()
350  {
351  auto& holder = getInstance();
352  const QReadLocker locker(&holder.mLock);
353 
354  for (const auto& mock : qAsConst(holder.mInstancesCreator))
355  {
356  if (mock.dynamicCast<FuncWrapper<T, Args ...>>())
357  {
358  return mock->getCounter();
359  }
360  }
361 
362  return -1; // There is no mock... use setCreator!
363  }
364 
365 
366  template<typename T, typename ... Args>
367  static void setCreator(std::function<T(Args ...)> pFunc)
368  {
369  Q_ASSERT(pFunc);
370 
371  const auto& value = QSharedPointer<FuncWrapper<T, Args ...>>::create(std::move(pFunc));
372 
373  auto& holder = getInstance();
374  const QWriteLocker locker(&holder.mLock);
375 
376  QMutableVectorIterator<Wrapper> iter(holder.mInstancesCreator);
377  while (iter.hasNext())
378  {
379  iter.next();
380  if (iter.value().dynamicCast<FuncWrapper<T, Args ...>>())
381  {
382  iter.setValue(value);
383  return;
384  }
385  }
386 
387  holder.mInstancesCreator << value;
388  }
389 
390 
391  static void setShared(const QMetaObject& pMetaObject, const QSharedPointer<QObject>& pObject);
392 #endif
393 
394 };
395 
396 } // namespace governikus
governikus::singleton
T * singleton()
governikus::Env::getSingleton
static T * getSingleton()
Definition: Env.h:301
governikus::Env::getShared
static QSharedPointer< T > getShared()
Definition: Env.h:315
governikus::Env::clear
static void clear()
Definition: Env.cpp:46
governikus::Env::init
static void init()
Definition: Env.h:294
defineSingleton
defineSingleton(Env) namespace
Definition: Env.cpp:11
governikus::Env::resetCounter
static void resetCounter()
Definition: Env.cpp:37
governikus::Env::set
static void set(const QMetaObject &pMetaObject, void *pObject=nullptr)
Definition: Env.cpp:59
governikus::Env
Definition: Env.h:44
governikus::Env::setShared
static void setShared(const QMetaObject &pMetaObject, const QSharedPointer< QObject > &pObject)
Definition: Env.cpp:80
governikus::Env::create
static T create(Args &&... pArgs)
Definition: Env.h:308
governikus::Env::~Env
~Env()=default
governikus
Implementation of ActivationContext for Intent based activation on Android systems.
Definition: ActivationContext.h:15
governikus::createNewObject
T createNewObject(Args &&... pArgs)
Env.h
governikus::Env::Env
Env()
Definition: Env.cpp:27
governikus::Env::ThreadSafe
Definition: Env.h:46
T
#define T(v)
Definition: http_parser.cpp:237
governikus::Env::setCreator
static void setCreator(std::function< T(Args ...)> pFunc)
Definition: Env.h:367
SingletonHelper.h
governikus::Env::getCounter
static int getCounter()
Definition: Env.h:349