#pragma once namespace CamelotFramework { struct CM_EXPORT GameObjectHandleData { GameObjectHandleData() :mPtr(nullptr) { } GameObjectHandleData(const std::shared_ptr& ptr) { mPtr = ptr; } std::shared_ptr mPtr; }; /** * @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: GameObjectHandleBase(const std::shared_ptr data); /** * @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; } GameObject* get() const { throwIfDestroyed(); return mData->mPtr.get(); } std::shared_ptr getInternalPtr() const { throwIfDestroyed(); return mData->mPtr; } GameObject* operator->() const { return get(); } GameObject& operator*() const { return *get(); } protected: friend SceneObject; GameObjectHandleBase(); inline void throwIfDestroyed() const; void destroy() { mData->mPtr = nullptr; } std::shared_ptr mData; }; // NOTE: It is important this class contains no data since we often value // cast it to its base 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; } inline operator GameObjectHandleBase() { GameObjectHandleBase base(mData); return base; } T* get() const { throwIfDestroyed(); return reinterpret_cast(mData->mPtr.get()); } std::shared_ptr getInternalPtr() const { throwIfDestroyed(); return std::static_pointer_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(const std::shared_ptr ptr) :GameObjectHandleBase() { mData = cm_shared_ptr(std::static_pointer_cast(ptr)); } }; 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)); } }