#pragma once namespace CamelotFramework { struct CM_EXPORT GameObjectHandleData { GameObjectHandleData() :mPtr(nullptr), mDeleter(nullptr) { } GameObjectHandleData(GameObject* ptr, void(*deleter)(GameObject*)) { mPtr = ptr; mDeleter = deleter; } typedef void(*HandleDeleter)(GameObject*); GameObject* mPtr; HandleDeleter mDeleter; }; /** * @brief A handle that can point to various types of game objects. * It primarily keeps track if the object is still alive, so anything * still referencing it doesn't accidentally use it. * * @note This class exists because I want the references between game objects be quite loose. * For example one game object should be able to reference another one without the other * one knowing. But if that is the case I also need to handle the case when the other * object we're referencing has been deleted, and that is the main purpose of this class. * */ class CM_EXPORT GameObjectHandleBase { public: /** * @brief Checks if the object has been destroyed */ bool isDestroyed() const { return mData->mPtr == nullptr; } /** * @brief Internal method only. Not meant to be called directly. */ std::shared_ptr getHandleData() const { return mData; } protected: friend SceneObject; GameObjectHandleBase(); inline void throwIfDestroyed() const; void destroy() { if(mData->mPtr != nullptr) { GameObject* toDelete = mData->mPtr; mData->mPtr = nullptr; // Need to set this to null before deleting, otherwise destructor thinks object isn't destroyed // and we end up here again if(mData->mDeleter != nullptr) mData->mDeleter(toDelete); else delete toDelete; } } std::shared_ptr mData; }; template class GameObjectHandle : public GameObjectHandleBase { public: GameObjectHandle() :GameObjectHandleBase() { mData = cm_shared_ptr(); } template GameObjectHandle(const GameObjectHandle& ptr) :GameObjectHandleBase() { mData = ptr.getHandleData(); } inline GameObjectHandle& operator=(std::nullptr_t ptr) { mData = cm_shared_ptr(); mData->mPtr = nullptr; return *this; } T* get() const { throwIfDestroyed(); return reinterpret_cast(mData->mPtr); } T* operator->() const { return get(); } T& operator*() const { return *get(); } 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->mPtr != nullptr)) ? &CM_Bool_struct::_Member : 0); } private: friend SceneObject; explicit GameObjectHandle(T* ptr, void(*deleter)(GameObject*) = nullptr) :GameObjectHandleBase() { mData = cm_shared_ptr((GameObject*)ptr, deleter); } }; template GameObjectHandle<_Ty1> static_object_cast(const GameObjectHandle<_Ty2>& other) { return GameObjectHandle<_Ty1>(other); } template bool operator==(const GameObjectHandle<_Ty1>& _Left, const GameObjectHandle<_Ty2>& _Right) { return (_Left.get() == _Right.get()); } template bool operator!=(const GameObjectHandle<_Ty1>& _Left, const GameObjectHandle<_Ty2>& _Right) { return (!(_Left == _Right)); } }