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:
getCounter()60 				[[nodiscard]] inline int getCounter() const
61 				{
62 					return mCounter;
63 				}
64 
65 
reset()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:
FuncWrapper(std::function<T (Args...)> pFunc)83 				explicit FuncWrapper(std::function<T(Args ...)> pFunc)
84 					: mFunc(std::move(pFunc))
85 				{
86 				}
87 
88 
operator()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>
createSingleton()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>
fetchRealSingleton()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>
checkObjectInfo(Identifier pId,T * pObject)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>
checkObjectInfo(Identifier pId,T * pObject)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>
fetchSingleton()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>
newObject(Args &&...pArgs)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>
createObject(Args &&...pArgs)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 
initialize()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:
init()294 		static void init()
295 		{
296 			getInstance().initialize();
297 		}
298 
299 
300 		template<typename T>
getSingleton()301 		static T* getSingleton()
302 		{
303 			return getInstance().fetchSingleton<T>();
304 		}
305 
306 
307 		template<typename T, typename ... Args>
create(Args &&...pArgs)308 		static T create(Args&& ... pArgs)
309 		{
310 			return getInstance().createObject<T>(std::forward<Args>(pArgs) ...);
311 		}
312 
313 
314 		template<typename T>
getShared()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>
getCounter()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>
setCreator(std::function<T (Args...)> pFunc)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
397