#pragma once #include "CmIReflectable.h" namespace CamelotFramework { template class ResourceHandle; struct CM_EXPORT ResourceHandleData { ResourceHandleData() :mIsCreated(false) { } std::shared_ptr mPtr; String mUUID; bool mIsCreated; }; class CM_EXPORT ResourceHandleBase : public IReflectable { public: /** * @brief Checks if the resource is loaded */ bool isLoaded() const; /** * @brief Blocks the current thread until the resource is fully loaded. * * @note Careful not to call this on the thread that does the loading. */ void synchronize() const; /** * @brief Returns the UUID of the resource the handle is referring to. */ const String& getUUID() const { return mData != nullptr ? mData->mUUID : StringUtil::BLANK; } /** * @brief Gets the handle data. For internal use only. */ const std::shared_ptr& getHandleData() const { return mData; } protected: ResourceHandleBase(); std::shared_ptr mData; /** * @brief Sets the created flag to true and assigns the resource pointer. Called * by the constructors, or if you constructed just using a UUID, then you need to * call this manually before you can access the resource from this handle. * * @note Two set construction is sometimes required due to multithreaded nature of resource loading. */ void setResourcePtr(std::shared_ptr ptr); private: friend class Resources; CM_STATIC_THREAD_SYNCHRONISER(mResourceCreatedCondition) CM_STATIC_MUTEX(mResourceCreatedMutex) protected: inline void throwIfNotLoaded() const; /************************************************************************/ /* RTTI */ /************************************************************************/ public: friend class ResourceHandleRTTI; static RTTITypeBase* getRTTIStatic(); virtual RTTITypeBase* getRTTI() const; }; template class ResourceHandle : public ResourceHandleBase { public: ResourceHandle() :ResourceHandleBase() { } // Note: This constructor requires you to call "resolve" with the actual resource pointer, // before the resource is considered as loaded ResourceHandle(const String& uuid) :ResourceHandleBase() { mData = cm_shared_ptr(); mData->mUUID = uuid; } template ResourceHandle(const ResourceHandle& ptr) :ResourceHandleBase() { mData = ptr.getHandleData(); } operator ResourceHandle() { return ResourceHandle(*this); } T* operator->() const { return get(); } T& operator*() const { return *get(); } ResourceHandle& operator=(std::nullptr_t ptr) { mData = nullptr; return *this; } template struct CM_Bool_struct { int _Member; }; // Conversion to bool // (Why not just directly convert to bool? Because then we can assign pointer to bool and that's weird) operator int CM_Bool_struct::*() const { return ((mData != nullptr && mData->mPtr != nullptr) ? &CM_Bool_struct::_Member : 0); } T* get() const { throwIfNotLoaded(); return reinterpret_cast(mData->mPtr.get()); } std::shared_ptr getInternalPtr() const { throwIfNotLoaded(); return std::static_pointer_cast(mData->mPtr); } private: friend class Resource; explicit ResourceHandle(T* ptr) :ResourceHandleBase() { mData = cm_shared_ptr(); setResourcePtr(std::shared_ptr(ptr)); } ResourceHandle(std::shared_ptr ptr) :ResourceHandleBase() { mData = cm_shared_ptr(); setResourcePtr(ptr); } }; template ResourceHandle<_Ty1> static_resource_cast(const ResourceHandle<_Ty2>& other) { return ResourceHandle<_Ty1>(other); } template bool operator==(const ResourceHandle<_Ty1>& _Left, const ResourceHandle<_Ty2>& _Right) { if(_Left.getHandleData() != nullptr && _Right.getHandleData() != nullptr) return _Left.getHandleData()->mPtr == _Right.getHandleData()->mPtr; return _Left.getHandleData() == _Right.getHandleData(); } template bool operator!=(const ResourceHandle<_Ty1>& _Left, const ResourceHandle<_Ty2>& _Right) { return (!(_Left == _Right)); } }