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