AusweisApp2
Env.h
gehe zur Dokumentation dieser Datei
1 /*
2  * \brief Runtime environment to create (mockable) objects.
3  *
4  * \copyright Copyright (c) 2017-2020 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 <QReadLocker>
19 #include <QReadWriteLock>
20 #include <QSharedPointer>
21 #include <QThread>
22 #include <QWeakPointer>
23 #include <QWriteLocker>
24 
25 #ifndef QT_NO_DEBUG
26 #include <QMutableVectorIterator>
27 #include <QVector>
28 #endif
29 
30 class test_Env;
31 
32 namespace governikus
33 {
34 
35 template<typename T> T* singleton();
36 template<typename T, typename ... Args> T createNewObject(Args&& ... pArgs);
37 
38 class Env
39 {
40  public:
41  struct ThreadSafe {};
42 
43  private:
44  friend class ::test_Env;
45  Q_DISABLE_COPY(Env)
46  using Identifier = const char*;
47 
48 #ifndef QT_NO_DEBUG
49  class FuncWrapperBase
50  {
51  protected:
52  int mCounter = 0;
53 
54  public:
55  inline int getCounter() const
56  {
57  return mCounter;
58  }
59 
60 
61  inline void reset()
62  {
63  mCounter = 0;
64  }
65 
66 
67  virtual ~FuncWrapperBase();
68  };
69 
70  template<typename T, typename ... Args>
71  class FuncWrapper final
72  : public FuncWrapperBase
73  {
74  private:
75  const std::function<T(Args ...)> mFunc;
76 
77  public:
78  FuncWrapper(std::function<T(Args ...)> pFunc)
79  : mFunc(std::move(pFunc))
80  {
81  }
82 
83 
84  T operator()(Args&& ... pArgs)
85  {
86  ++mCounter;
87  return mFunc(std::forward<Args>(pArgs) ...);
88  }
89 
90 
91  };
92 
93  using Wrapper = QSharedPointer<FuncWrapperBase>;
94  QVector<Wrapper> mInstancesCreator;
95  QMap<Identifier, void*> mInstancesSingleton;
96  mutable QReadWriteLock mLock;
97 #endif
98 
99  QMap<Identifier, QWeakPointer<QObject>> mSharedInstances;
100  mutable QReadWriteLock mSharedInstancesLock;
101 
102  static Env& getInstance();
103 
104  template<typename T>
105  inline T* fetchRealSingleton()
106  {
107  if constexpr (std::is_abstract<T>::value && std::is_destructible<T>::value)
108  {
109  static_assert(std::has_virtual_destructor<T>::value, "Destructor must be virtual");
110  return singleton<T>();
111  }
112  else
113  {
114  return &T::getInstance();
115  }
116  }
117 
118 
119  template<typename T>
120  #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
121  inline typename std::enable_if<QtPrivate::IsGadgetHelper<T>::IsRealGadget, T*>::type checkObjectInfo(Identifier pId, T* pObject) const
122 
123  #else
124  inline typename std::enable_if<QtPrivate::IsGadgetHelper<T>::Value, T*>::type checkObjectInfo(Identifier pId, T* pObject) const
125  #endif
126  {
127  Q_UNUSED(pId)
128  return pObject;
129  }
130 
131 
132  template<typename T>
133  inline typename std::enable_if<QtPrivate::IsPointerToTypeDerivedFromQObject<T*>::Value, T*>::type checkObjectInfo(Identifier pId, T* pObject) const
134  {
135  if (!std::is_base_of<ThreadSafe, T>() && pObject->thread() != QThread::currentThread())
136  {
137  qWarning() << pId << "was created in" << pObject->thread()->objectName() << "but is requested by" << QThread::currentThread()->objectName();
138 #ifndef QT_NO_DEBUG
139  Q_ASSERT(QCoreApplication::applicationName().startsWith(QLatin1String("Test_global_Env")));
140 #endif
141  }
142 
143  return pObject;
144  }
145 
146 
147  template<typename T>
148  inline T* fetchSingleton()
149  {
150  #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
151  static_assert(QtPrivate::IsGadgetHelper<T>::IsRealGadget || QtPrivate::IsPointerToTypeDerivedFromQObject<T*>::Value,
152  "Singletons needs to be a Q_GADGET or an QObject/Q_OBJECT");
153  #else
154  static_assert(QtPrivate::IsGadgetHelper<T>::Value || QtPrivate::IsPointerToTypeDerivedFromQObject<T*>::Value,
155  "Singletons needs to be a Q_GADGET or an QObject/Q_OBJECT");
156  #endif
157 
158  const Identifier id = T::staticMetaObject.className();
159  void* obj = nullptr;
160 #ifndef QT_NO_DEBUG
161  const QReadLocker locker(&mLock);
162  obj = mInstancesSingleton.value(id);
163  if (!obj)
164 #endif
165  obj = fetchRealSingleton<T>();
166  Q_ASSERT(obj);
167  return checkObjectInfo(id, static_cast<T*>(obj));
168  }
169 
170 
171  template<typename T, typename ... Args>
172  inline T newObject(Args&& ... pArgs) const
173  {
174  if constexpr (std::is_constructible<typename std::remove_pointer<T>::type, Args ...>::value)
175  {
176  if constexpr (std::is_pointer<T>::value)
177  {
178  using t = typename std::remove_pointer<T>::type;
179  return new t(std::forward<Args>(pArgs) ...);
180  }
181  else
182  {
183  return T(std::forward<Args>(pArgs) ...);
184  }
185  }
186  else
187  {
188  static_assert(std::is_pointer<T>::value, "It is impossible to return implementation of interface by value. Use pointer or add constructor!");
189  auto obj = createNewObject<T>(std::forward<Args>(pArgs) ...);
190  Q_ASSERT(obj);
191  return obj;
192  }
193  }
194 
195 
196  template<typename T, typename ... Args>
197  T createObject(Args&& ... pArgs) const
198  {
199 #ifndef QT_NO_DEBUG
200  {
201  QReadLocker locker(&mLock);
202 
203  // copy QSharedPointer "mock" to increase ref-counter. Otherwise
204  // unlock would allow to delete the wrapper.
205  for (auto mock : qAsConst(mInstancesCreator)) // clazy:exclude=range-loop
206  {
207  auto creator = mock.dynamicCast<FuncWrapper<T, Args ...>>();
208  if (creator)
209  {
210  locker.unlock();
211  return (*creator)(std::forward<Args>(pArgs) ...);
212  }
213  }
214  }
215 #endif
216 
217  return newObject<T>(std::forward<Args>(pArgs) ...);
218  }
219 
220  protected:
221  Env();
222  ~Env() = default;
223 
224  public:
225  template<typename T>
226  static T* getSingleton()
227  {
228  return getInstance().fetchSingleton<T>();
229  }
230 
231 
232  template<typename T, typename ... Args>
233  static T create(Args&& ... pArgs)
234  {
235  return getInstance().createObject<T>(std::forward<Args>(pArgs) ...);
236  }
237 
238 
239  template<typename T>
240  static QSharedPointer<T> getShared()
241  {
242  #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
243  static_assert(QtPrivate::IsGadgetHelper<T>::IsRealGadget || QtPrivate::IsPointerToTypeDerivedFromQObject<T*>::Value,
244  "Shared class needs to be a Q_GADGET or an QObject/Q_OBJECT");
245  #else
246  static_assert(QtPrivate::IsGadgetHelper<T>::Value || QtPrivate::IsPointerToTypeDerivedFromQObject<T*>::Value,
247  "Shared class needs to be a Q_GADGET or an QObject/Q_OBJECT");
248  #endif
249 
250  const Identifier className = T::staticMetaObject.className();
251 
252  auto& holder = getInstance();
253  holder.mSharedInstancesLock.lockForRead();
254  QSharedPointer<T> shared = qSharedPointerCast<T>(holder.mSharedInstances.value(className));
255  holder.mSharedInstancesLock.unlock();
256 
257  if (!shared)
258  {
259  const QWriteLocker locker(&holder.mSharedInstancesLock);
260  shared = qSharedPointerCast<T>(holder.mSharedInstances.value(className));
261  if (!shared)
262  {
263  qDebug() << "Spawn shared instance:" << className;
264  shared = QSharedPointer<T>::create();
265  holder.mSharedInstances.insert(className, shared.toWeakRef());
266  }
267  }
268 
269  return shared;
270  }
271 
272 
273 #ifndef QT_NO_DEBUG
274  static void resetCounter();
275  static void clear();
276  static void set(const QMetaObject& pMetaObject, void* pObject = nullptr);
277 
278  template<typename T, typename ... Args>
279  static int getCounter()
280  {
281  auto& holder = getInstance();
282  const QReadLocker locker(&holder.mLock);
283 
284  for (const auto& mock : qAsConst(holder.mInstancesCreator))
285  {
286  if (mock.dynamicCast<FuncWrapper<T, Args ...>>())
287  {
288  return mock->getCounter();
289  }
290  }
291 
292  return -1; // There is no mock... use setCreator!
293  }
294 
295 
296  template<typename T, typename ... Args>
297  static void setCreator(std::function<T(Args ...)> pFunc)
298  {
299  Q_ASSERT(pFunc);
300 
301  const auto& value = QSharedPointer<FuncWrapper<T, Args ...>>::create(std::move(pFunc));
302 
303  auto& holder = getInstance();
304  const QWriteLocker locker(&holder.mLock);
305 
306  QMutableVectorIterator<Wrapper> iter(holder.mInstancesCreator);
307  while (iter.hasNext())
308  {
309  iter.next();
310  if (iter.value().dynamicCast<FuncWrapper<T, Args ...>>())
311  {
312  iter.setValue(value);
313  return;
314  }
315  }
316 
317  holder.mInstancesCreator << value;
318  }
319 
320 
321  static void setShared(const QMetaObject& pMetaObject, const QSharedPointer<QObject>& pObject);
322 #endif
323 
324 };
325 
326 } // namespace governikus
governikus::singleton
T * singleton()
governikus::Env::getSingleton
static T * getSingleton()
Definition: Env.h:226
governikus::Env::getShared
static QSharedPointer< T > getShared()
Definition: Env.h:240
governikus::Env::Env
Env()
governikus::Env::clear
static void clear()
Definition: Env.cpp:38
governikus::Env::resetCounter
static void resetCounter()
Definition: Env.cpp:29
governikus::Env::set
static void set(const QMetaObject &pMetaObject, void *pObject=nullptr)
Definition: Env.cpp:51
governikus::Env
Definition: Env.h:39
governikus::Env::setShared
static void setShared(const QMetaObject &pMetaObject, const QSharedPointer< QObject > &pObject)
Definition: Env.cpp:72
governikus::Env::create
static T create(Args &&... pArgs)
Definition: Env.h:233
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::ThreadSafe
Definition: Env.h:41
defineSingleton
defineSingleton(Env) Env
Definition: Env.cpp:11
test_Env
Definition: test_Env.cpp:290
T
#define T(v)
Definition: http_parser.cpp:237
governikus::Env::setCreator
static void setCreator(std::function< T(Args ...)> pFunc)
Definition: Env.h:297
SingletonHelper.h
governikus::Env::getCounter
static int getCounter()
Definition: Env.h:279