/* * \brief Runtime environment to create (mockable) objects. * * \copyright Copyright (c) 2017 Governikus GmbH & Co. KG, Germany */ #pragma once #include #include #include #include #include #include #include #include #ifndef QT_NO_DEBUG #include #include #endif class test_Env; namespace governikus { template T* singleton(bool& pTakeOwnership); template T createNewObject(Args&& ... pArgs); class Env { private: friend class ::test_Env; Q_DISABLE_COPY(Env) #ifndef QT_NO_DEBUG class FuncWrapperBase { protected: int mCounter = 0; public: inline int getCounter() const { return mCounter; } inline void reset() { mCounter = 0; } virtual ~FuncWrapperBase(); }; template class FuncWrapper final : public FuncWrapperBase { private: const std::function mFunc; public: FuncWrapper(const std::function& pFunc) : mFunc(pFunc) { } virtual ~FuncWrapper() { } T operator()(Args&& ... pArgs) { ++mCounter; return mFunc(std::forward(pArgs) ...); } }; using Wrapper = std::shared_ptr; QVector mInstancesCreator; #endif enum class Type { UNDEFINED, OWNERSHIP, UNMANAGED }; using Identifier = const char*; QMap mTypeInfo; QMap mInstancesUnmanaged; QMap > mInstancesOwnership; QMap > mSharedInstances; static Env& getInstance(); void storeSingleton(Identifier pId, void* pObject); void storeSingleton(Identifier pId, std::shared_ptr pObject); void removeStoredSingleton(Identifier pId); void* fetchStoredSingleton(Identifier pId) const; template typename std::enable_if::value && std::is_destructible::value, T*>::type storeSingleton(Identifier pId) { static_assert(std::has_virtual_destructor::value, "Destructor must be virtual"); bool ownership = true; T* obj = singleton(ownership); Q_ASSERT(obj); if (ownership) { storeSingleton(pId, std::shared_ptr(obj)); } else { storeSingleton(pId, obj); } return obj; } template typename std::enable_if::value, T*>::type storeSingleton(Identifier pId) { T* obj = &T::getInstance(); storeSingleton(pId, obj); return obj; } template typename std::enable_if::value, T*>::type storeSingleton(Identifier pId) { auto obj = std::make_shared(); storeSingleton(pId, obj); return obj.get(); } template inline T* fetchSingleton() { static_assert(QtPrivate::IsGadgetHelper::Value || QtPrivate::IsPointerToTypeDerivedFromQObject::Value, "Singletons needs to be a Q_GADGET or an QObject/Q_OBJECT"); Identifier id = T::staticMetaObject.className(); void* obj = fetchStoredSingleton(id); if (!obj) { obj = storeSingleton(id); } return static_cast(obj); } template inline typename std::enable_if::type, Args ...>::value, T>::type newObject(Args&& ... pArgs) const { static_assert(std::is_pointer::value, "It is impossible to return implementation of interface by value. Use pointer or add constructor!"); auto obj = createNewObject(std::forward(pArgs) ...); Q_ASSERT(obj); return obj; } template inline typename std::enable_if::value, T>::type internalNewObject(Args&& ... pArgs) const { using t = typename std::remove_pointer::type; return new t(std::forward(pArgs) ...); } template inline typename std::enable_if::value, T>::type internalNewObject(Args&& ... pArgs) const { return T(std::forward(pArgs) ...); } template inline typename std::enable_if::type, Args ...>::value, T>::type newObject(Args&& ... pArgs) const { return internalNewObject(std::forward(pArgs) ...); } template T createObject(Args&& ... pArgs) const { #ifndef QT_NO_DEBUG for (auto& mock : qAsConst(mInstancesCreator)) { auto creator = dynamic_cast*>(mock.get()); if (creator) { return (*creator)(std::forward(pArgs) ...); } } #endif return newObject(std::forward(pArgs) ...); } protected: Env(); ~Env(); public: template static T* getSingleton() { return getInstance().fetchSingleton(); } template static T create(Args&& ... pArgs) { return getInstance().createObject(std::forward(pArgs) ...); } template static QSharedPointer getShared() { static_assert(QtPrivate::IsGadgetHelper::Value || QtPrivate::IsPointerToTypeDerivedFromQObject::Value, "Shared class needs to be a Q_GADGET or an QObject/Q_OBJECT"); auto& holder = getInstance().mSharedInstances; const auto* className = T::staticMetaObject.className(); QSharedPointer shared = qSharedPointerCast(holder.value(className)); if (!shared) { shared = QSharedPointer::create(); holder.insert(className, shared.toWeakRef()); } return shared; } #ifndef QT_NO_DEBUG static void resetCounter(); static void clear(); static void set(const QMetaObject& pMetaObject, void* pObject = nullptr); static void set(const QMetaObject& pMetaObject, std::shared_ptr pObject); template static U* getSingleton() { return dynamic_cast(getSingleton()); } template static int getCounter() { for (const auto& mock : qAsConst(getInstance().mInstancesCreator)) { if (dynamic_cast*>(mock.get())) { return mock->getCounter(); } } return -1; // There is no mock... use setCreator! } template static void setCreator(const std::function& pFunc) { Q_ASSERT(pFunc); auto& holder = getInstance().mInstancesCreator; const auto& value = Wrapper(new FuncWrapper(pFunc)); QMutableVectorIterator iter(holder); while (iter.hasNext()) { iter.next(); if (dynamic_cast*>(iter.value().get())) { iter.setValue(value); return; } } holder << value; } static void setShared(const QMetaObject& pMetaObject, QSharedPointer pObject = QSharedPointer()); #endif }; } /* namespace governikus */