AusweisApp2
Lade ...
Suche ...
Keine Treffer
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
35class test_Env;
36
37namespace governikus
38{
39
40template<typename T> T* singleton();
41template<typename T, typename ... Args> T createNewObject(Args&& ... pArgs);
42
43class 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
Definition: Env.h:44
static QSharedPointer< T > getShared()
Definition: Env.h:315
static void setCreator(std::function< T(Args ...)> pFunc)
Definition: Env.h:367
static int getCounter()
Definition: Env.h:349
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 T * getSingleton()
Definition: Env.h:301
static void resetCounter()
Definition: Env.cpp:37
static T create(Args &&... pArgs)
Definition: Env.h:308
static void init()
Definition: Env.h:294
~Env()=default
static void setShared(const QMetaObject &pMetaObject, const QSharedPointer< QObject > &pObject)
Definition: Env.cpp:80
#define T(v)
Definition: http_parser.cpp:237
Implementation of ActivationContext for Intent based activation on Android systems.
Definition: ActivationContext.h:15
T * singleton()
T createNewObject(Args &&... pArgs)
Definition: Env.h:46