From 4c4822eef905fdf08db173d63ba42e2bc4ec94b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Leclercq?= Date: Fri, 12 Oct 2018 15:46:40 +0200 Subject: [PATCH] Core/ObjectHandle: Remade object handle system --- ChangeLog.md | 2 + SDK/include/NDK/Entity.hpp | 2 +- SDK/include/NDK/EntityOwner.hpp | 1 + SDK/include/NDK/EntityOwner.inl | 17 +++- include/Nazara/Core/HandledObject.hpp | 17 +++- include/Nazara/Core/HandledObject.inl | 76 +++++--------- include/Nazara/Core/ObjectHandle.hpp | 13 +-- include/Nazara/Core/ObjectHandle.inl | 141 ++++++-------------------- src/Nazara/Core/HandledObject.cpp | 18 ++++ 9 files changed, 107 insertions(+), 180 deletions(-) create mode 100644 src/Nazara/Core/HandledObject.cpp diff --git a/ChangeLog.md b/ChangeLog.md index a49c7d583..43f8aa898 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -151,6 +151,7 @@ Nazara Engine: - Added TcpClient::PollForConnected - ⚠️ Use of the new Angle class instead of floating point angle - It is now possible to set elasticity/friction/surface bodies of 2D colliders and change it at runtime on RigidBody2D +- ObjectHandle were remade and should be way more optimized now Nazara Development Kit: - Added ImageWidget (#139) @@ -219,6 +220,7 @@ Nazara Development Kit: - EntityOwner constructor taking a Entity* is no longer explicit - PhysicsComponent2D now allows massless bodies (zero mass) - ⚠️ Use of the new Angle class instead of floating point angle +- Added EntityOwner::Release # 0.4: diff --git a/SDK/include/NDK/Entity.hpp b/SDK/include/NDK/Entity.hpp index 3cbe4785c..43a4a43c7 100644 --- a/SDK/include/NDK/Entity.hpp +++ b/SDK/include/NDK/Entity.hpp @@ -8,8 +8,8 @@ #define NDK_ENTITY_HPP #include -#include #include +#include #include #include #include diff --git a/SDK/include/NDK/EntityOwner.hpp b/SDK/include/NDK/EntityOwner.hpp index 0be491058..84eb95feb 100644 --- a/SDK/include/NDK/EntityOwner.hpp +++ b/SDK/include/NDK/EntityOwner.hpp @@ -20,6 +20,7 @@ namespace Ndk EntityOwner(EntityOwner&& handle) noexcept = default; ~EntityOwner(); + void Release(); void Reset(Entity* entity = nullptr); void Reset(EntityOwner&& handle); diff --git a/SDK/include/NDK/EntityOwner.inl b/SDK/include/NDK/EntityOwner.inl index 566962fb0..4ee9530f7 100644 --- a/SDK/include/NDK/EntityOwner.inl +++ b/SDK/include/NDK/EntityOwner.inl @@ -2,6 +2,7 @@ // This file is part of the "Nazara Development Kit" // For conditions of distribution and use, see copyright notice in Prerequisites.hpp +#include #include #include #include @@ -36,16 +37,23 @@ namespace Ndk Reset(nullptr); } + /*! + * \brief Release the ownership of the entity without killing it + */ + inline void EntityOwner::Release() + { + EntityHandle::Reset(nullptr); + } + /*! * \brief Resets the ownership of the entity, previous is killed * * \param entity Entity to own */ - inline void EntityOwner::Reset(Entity* entity) { - if (m_object) - m_object->Kill(); + if (IsValid()) + GetObject()->Kill(); EntityHandle::Reset(entity); } @@ -55,11 +63,10 @@ namespace Ndk * * \param handle EntityOwner to move into this */ - inline void EntityOwner::Reset(EntityOwner&& handle) { Reset(handle.GetObject()); - handle.m_object = nullptr; + handle.Release(); } /*! diff --git a/include/Nazara/Core/HandledObject.hpp b/include/Nazara/Core/HandledObject.hpp index 633f7e9fe..27c8646b3 100644 --- a/include/Nazara/Core/HandledObject.hpp +++ b/include/Nazara/Core/HandledObject.hpp @@ -13,6 +13,16 @@ namespace Nz { + namespace Detail + { + struct NAZARA_CORE_API HandleData + { + void* object; + + static std::shared_ptr GetEmptyObject(); + }; + } + template class ObjectHandle; template @@ -35,11 +45,10 @@ namespace Nz void UnregisterAllHandles() noexcept; private: - void RegisterHandle(ObjectHandle* handle); - void UnregisterHandle(ObjectHandle* handle) noexcept; - void UpdateHandle(ObjectHandle* oldHandle, ObjectHandle* newHandle) noexcept; + std::shared_ptr GetHandleData(); + void InitHandleData(); - std::vector*> m_handles; + std::shared_ptr m_handleData; }; } diff --git a/include/Nazara/Core/HandledObject.inl b/include/Nazara/Core/HandledObject.inl index 3349fa7e1..bc11ec645 100644 --- a/include/Nazara/Core/HandledObject.inl +++ b/include/Nazara/Core/HandledObject.inl @@ -2,9 +2,10 @@ // This file is part of the "Nazara Development Kit" // For conditions of distribution and use, see copyright notice in Prerequisites.hpp +#include #include -#include #include +#include #include #include @@ -35,10 +36,10 @@ namespace Nz */ template HandledObject::HandledObject(HandledObject&& object) noexcept : - m_handles(std::move(object.m_handles)) + m_handleData(std::move(object.m_handleData)) { - for (ObjectHandle* handle : m_handles) - handle->OnObjectMoved(static_cast(this)); + if (m_handleData) + m_handleData->object = static_cast(this); } /*! @@ -74,7 +75,7 @@ namespace Nz NazaraUnused(object); // Nothing to do - return *this; + return *this; } /*! @@ -88,73 +89,42 @@ namespace Nz { UnregisterAllHandles(); - m_handles = std::move(object.m_handles); - for (ObjectHandle* handle : m_handles) - handle->OnObjectMoved(static_cast(this)); + m_handleData = std::move(object.m_handleData); + + if (m_handleData) + m_handleData->object = static_cast(this); return *this; } - /*! - * \brief Registers a handle - * - * \param handle Handle to register - * - * \remark One handle can only be registered once, errors can occur if it's more than once - */ - template - void HandledObject::RegisterHandle(ObjectHandle* handle) - { - m_handles.push_back(handle); - } - /*! * \brief Unregisters all handles */ template void HandledObject::UnregisterAllHandles() noexcept { - // Tell every handle we got destroyed, to null them - for (ObjectHandle* handle : m_handles) - handle->OnObjectDestroyed(); - - m_handles.clear(); + if (m_handleData) + { + m_handleData->object = nullptr; + m_handleData.reset(); + } } - /*! - * \brief Unregisters a handle - * - * \param handle Handle to unregister - * - * \remark One handle can only be unregistered once, crash can occur if it's more than once - * \remark Produces a NazaraAssert if handle not registered - */ template - void HandledObject::UnregisterHandle(ObjectHandle* handle) noexcept + std::shared_ptr HandledObject::GetHandleData() { - auto it = std::find(m_handles.begin(), m_handles.end(), handle); - NazaraAssert(it != m_handles.end(), "Handle not registered"); + if (!m_handleData) + InitHandleData(); - // Swap and pop idiom, more efficient than vector::erase - std::swap(*it, m_handles.back()); - m_handles.pop_back(); + return std::shared_ptr(m_handleData); } - /*! - * \brief Updates one handle with another - * - * \param oldHandle Old handle to replace - * \param newHandle New handle to take place - * - * \remark Produces a NazaraAssert if handle not registered - */ template - void HandledObject::UpdateHandle(ObjectHandle* oldHandle, ObjectHandle* newHandle) noexcept + void HandledObject::InitHandleData() { - auto it = std::find(m_handles.begin(), m_handles.end(), oldHandle); - NazaraAssert(it != m_handles.end(), "Handle not registered"); + assert(!m_handleData); - // Simply update the handle - *it = newHandle; + m_handleData = std::make_shared(); + m_handleData->object = static_cast(this); } } diff --git a/include/Nazara/Core/ObjectHandle.hpp b/include/Nazara/Core/ObjectHandle.hpp index 9dadbc832..e2e72f185 100644 --- a/include/Nazara/Core/ObjectHandle.hpp +++ b/include/Nazara/Core/ObjectHandle.hpp @@ -8,12 +8,12 @@ #define NAZARA_OBJECTHANDLE_HPP #include +#include +#include #include namespace Nz { - template class HandledObject; - template class ObjectHandle { @@ -22,7 +22,7 @@ namespace Nz public: ObjectHandle(); explicit ObjectHandle(T* object); - ObjectHandle(const ObjectHandle& handle); + ObjectHandle(const ObjectHandle& handle) = default; ObjectHandle(ObjectHandle&& handle) noexcept; ~ObjectHandle(); @@ -43,16 +43,13 @@ namespace Nz T* operator->() const; ObjectHandle& operator=(T* object); - ObjectHandle& operator=(const ObjectHandle& handle); + ObjectHandle& operator=(const ObjectHandle& handle) = default; ObjectHandle& operator=(ObjectHandle&& handle) noexcept; static const ObjectHandle InvalidHandle; protected: - void OnObjectDestroyed() noexcept; - void OnObjectMoved(T* newObject) noexcept; - - T* m_object; + std::shared_ptr m_handleData; }; template std::ostream& operator<<(std::ostream& out, const ObjectHandle& handle); diff --git a/include/Nazara/Core/ObjectHandle.inl b/include/Nazara/Core/ObjectHandle.inl index 0ba6afc2c..fc43fdffe 100644 --- a/include/Nazara/Core/ObjectHandle.inl +++ b/include/Nazara/Core/ObjectHandle.inl @@ -15,37 +15,15 @@ namespace Nz * \brief Core class that represents a object handle */ - /*! - * \brief Constructs a ObjectHandle object by default - */ - template - ObjectHandle::ObjectHandle() : - m_object(nullptr) - { - } - /*! * \brief Constructs a ObjectHandle object with a pointer to an object * * \param object Pointer to handle like an object (can be nullptr) */ template - ObjectHandle::ObjectHandle(T* object) : - ObjectHandle() + ObjectHandle::ObjectHandle() : + m_handleData(Detail::HandleData::GetEmptyObject()) { - Reset(object); - } - - /*! - * \brief Constructs a ObjectHandle object by assignation - * - * \param handle ObjectHandle to assign into this - */ - template - ObjectHandle::ObjectHandle(const ObjectHandle& handle) : - ObjectHandle() - { - Reset(handle); } /*! @@ -54,12 +32,22 @@ namespace Nz * \param handle ObjectHandle to move into this */ template - ObjectHandle::ObjectHandle(ObjectHandle&& handle) noexcept : - ObjectHandle() + ObjectHandle::ObjectHandle(ObjectHandle&& handle) noexcept { Reset(std::move(handle)); } + /*! + * \brief Constructs a ObjectHandle object with a pointer to an object + * + * \param object Pointer to handle like an object (can be nullptr) + */ + template + ObjectHandle::ObjectHandle(T* object) + { + Reset(object); + } + /*! * \brief Destructs the object and calls reset with nullptr * @@ -78,7 +66,7 @@ namespace Nz template T* ObjectHandle::GetObject() const { - return m_object; + return static_cast(m_handleData->object); } /*! @@ -88,7 +76,7 @@ namespace Nz template bool ObjectHandle::IsValid() const { - return m_object != nullptr; + return m_handleData->object != nullptr; } /*! @@ -99,14 +87,10 @@ namespace Nz template void ObjectHandle::Reset(T* object) { - // If we already have an entity, we must alert it that we are not pointing to it anymore - if (m_object) - m_object->UnregisterHandle(this); - - m_object = object; - if (m_object) - // We alert the new entity that we are pointing to it - m_object->RegisterHandle(this); + if (object) + m_handleData = object->GetHandleData(); + else + m_handleData = Detail::HandleData::GetEmptyObject(); } /*! @@ -117,7 +101,7 @@ namespace Nz template void ObjectHandle::Reset(const ObjectHandle& handle) { - Reset(handle.GetObject()); + m_handleData = handle.m_handleData; } /*! @@ -128,20 +112,8 @@ namespace Nz template void ObjectHandle::Reset(ObjectHandle&& handle) noexcept { - if (this == &handle) - return; - - if (m_object) - m_object->UnregisterHandle(this); - - if (T* object = handle.GetObject()) - { - m_object = handle.m_object; - handle.m_object = nullptr; - object->UpdateHandle(&handle, this); - } - else - m_object = nullptr; + m_handleData = std::move(handle.m_handleData); + handle.m_handleData = Detail::HandleData::GetEmptyObject(); } /*! @@ -153,23 +125,8 @@ namespace Nz template ObjectHandle& ObjectHandle::Swap(ObjectHandle& handle) { - // As we swap the two handles, we must alert the entities - // The default version with swap (move) would be working, - // but will register handles one more time (due to temporary copy). - if (m_object) - { - m_object->UnregisterHandle(this); - m_object->RegisterHandle(&handle); - } - - if (handle.m_object) - { - handle.m_object->UnregisterHandle(&handle); - handle.m_object->RegisterHandle(this); - } - // We do the swap - std::swap(m_object, handle.m_object); + std::swap(m_handleData, handle.m_handleData); return *this; } @@ -183,7 +140,7 @@ namespace Nz Nz::StringStream ss; ss << "ObjectHandle("; if (IsValid()) - ss << m_object->ToString(); + ss << GetObject()->ToString(); else ss << "Null"; @@ -211,7 +168,7 @@ namespace Nz template ObjectHandle::operator T*() const { - return m_object; + return GetObject(); } /*! @@ -221,7 +178,7 @@ namespace Nz template T* ObjectHandle::operator->() const { - return m_object; + return GetObject(); } /*! @@ -231,23 +188,9 @@ namespace Nz * \param entity Pointer to handle like an object (can be nullptr) */ template - ObjectHandle& ObjectHandle::operator=(T* entity) + ObjectHandle& ObjectHandle::operator=(T* object) { - Reset(entity); - - return *this; - } - - /*! - * \brief Sets the handle of the ObjectHandle with the handle from another - * \return A reference to this - * - * \param handle The other ObjectHandle - */ - template - ObjectHandle& ObjectHandle::operator=(const ObjectHandle& handle) - { - Reset(handle); + Reset(object); return *this; } @@ -266,26 +209,6 @@ namespace Nz return *this; } - /*! - * \brief Action to do on object destruction - */ - template - void ObjectHandle::OnObjectDestroyed() noexcept - { - // Shortcut - m_object = nullptr; - } - - /*! - * \brief Action to do on object move - */ - template - void ObjectHandle::OnObjectMoved(T* newObject) noexcept - { - // The object has been moved, update our pointer - m_object = newObject; - } - /*! * \brief Output operator * \return The stream @@ -388,7 +311,7 @@ namespace Nz template bool operator<(const ObjectHandle& lhs, const ObjectHandle& rhs) { - return lhs.m_object < rhs.m_object; + return lhs.GetObject() < rhs.GetObject(); } /*! @@ -401,7 +324,7 @@ namespace Nz template bool operator<(const T& lhs, const ObjectHandle& rhs) { - return &lhs < rhs.m_object; + return &lhs < rhs.GetObject(); } /*! @@ -414,7 +337,7 @@ namespace Nz template bool operator<(const ObjectHandle& lhs, const T& rhs) { - return lhs.m_object < &rhs; + return lhs.GetObject() < &rhs; } /*! diff --git a/src/Nazara/Core/HandledObject.cpp b/src/Nazara/Core/HandledObject.cpp new file mode 100644 index 000000000..664912a6c --- /dev/null +++ b/src/Nazara/Core/HandledObject.cpp @@ -0,0 +1,18 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + namespace Detail + { + std::shared_ptr HandleData::GetEmptyObject() + { + static std::shared_ptr emptyHandleData = std::make_shared(HandleData{}); + return emptyHandleData; + } + } +}