Core/ObjectHandle: Remade object handle system

This commit is contained in:
Jérôme Leclercq 2018-10-12 15:46:40 +02:00
parent 3933d5007d
commit 4c4822eef9
9 changed files with 107 additions and 180 deletions

View File

@ -151,6 +151,7 @@ Nazara Engine:
- Added TcpClient::PollForConnected - Added TcpClient::PollForConnected
- ⚠️ Use of the new Angle class instead of floating point angle - ⚠️ 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 - 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: Nazara Development Kit:
- Added ImageWidget (#139) - Added ImageWidget (#139)
@ -219,6 +220,7 @@ Nazara Development Kit:
- EntityOwner constructor taking a Entity* is no longer explicit - EntityOwner constructor taking a Entity* is no longer explicit
- PhysicsComponent2D now allows massless bodies (zero mass) - PhysicsComponent2D now allows massless bodies (zero mass)
- ⚠️ Use of the new Angle class instead of floating point angle - ⚠️ Use of the new Angle class instead of floating point angle
- Added EntityOwner::Release
# 0.4: # 0.4:

View File

@ -8,8 +8,8 @@
#define NDK_ENTITY_HPP #define NDK_ENTITY_HPP
#include <Nazara/Core/Bitset.hpp> #include <Nazara/Core/Bitset.hpp>
#include <Nazara/Core/HandledObject.hpp>
#include <Nazara/Core/MovablePtr.hpp> #include <Nazara/Core/MovablePtr.hpp>
#include <Nazara/Core/ObjectHandle.hpp>
#include <Nazara/Core/Signal.hpp> #include <Nazara/Core/Signal.hpp>
#include <NDK/Algorithm.hpp> #include <NDK/Algorithm.hpp>
#include <NDK/Prerequisites.hpp> #include <NDK/Prerequisites.hpp>

View File

@ -20,6 +20,7 @@ namespace Ndk
EntityOwner(EntityOwner&& handle) noexcept = default; EntityOwner(EntityOwner&& handle) noexcept = default;
~EntityOwner(); ~EntityOwner();
void Release();
void Reset(Entity* entity = nullptr); void Reset(Entity* entity = nullptr);
void Reset(EntityOwner&& handle); void Reset(EntityOwner&& handle);

View File

@ -2,6 +2,7 @@
// This file is part of the "Nazara Development Kit" // This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp // For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NDK/EntityOwner.hpp>
#include <Nazara/Core/StringStream.hpp> #include <Nazara/Core/StringStream.hpp>
#include <functional> #include <functional>
#include <limits> #include <limits>
@ -36,16 +37,23 @@ namespace Ndk
Reset(nullptr); 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 * \brief Resets the ownership of the entity, previous is killed
* *
* \param entity Entity to own * \param entity Entity to own
*/ */
inline void EntityOwner::Reset(Entity* entity) inline void EntityOwner::Reset(Entity* entity)
{ {
if (m_object) if (IsValid())
m_object->Kill(); GetObject()->Kill();
EntityHandle::Reset(entity); EntityHandle::Reset(entity);
} }
@ -55,11 +63,10 @@ namespace Ndk
* *
* \param handle EntityOwner to move into this * \param handle EntityOwner to move into this
*/ */
inline void EntityOwner::Reset(EntityOwner&& handle) inline void EntityOwner::Reset(EntityOwner&& handle)
{ {
Reset(handle.GetObject()); Reset(handle.GetObject());
handle.m_object = nullptr; handle.Release();
} }
/*! /*!

View File

@ -13,6 +13,16 @@
namespace Nz namespace Nz
{ {
namespace Detail
{
struct NAZARA_CORE_API HandleData
{
void* object;
static std::shared_ptr<HandleData> GetEmptyObject();
};
}
template<typename T> class ObjectHandle; template<typename T> class ObjectHandle;
template<typename T> template<typename T>
@ -35,11 +45,10 @@ namespace Nz
void UnregisterAllHandles() noexcept; void UnregisterAllHandles() noexcept;
private: private:
void RegisterHandle(ObjectHandle<T>* handle); std::shared_ptr<const Detail::HandleData> GetHandleData();
void UnregisterHandle(ObjectHandle<T>* handle) noexcept; void InitHandleData();
void UpdateHandle(ObjectHandle<T>* oldHandle, ObjectHandle<T>* newHandle) noexcept;
std::vector<ObjectHandle<T>*> m_handles; std::shared_ptr<Detail::HandleData> m_handleData;
}; };
} }

View File

@ -2,9 +2,10 @@
// This file is part of the "Nazara Development Kit" // This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp // For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <Nazara/Core/HandledObject.hpp>
#include <Nazara/Core/Error.hpp> #include <Nazara/Core/Error.hpp>
#include <Nazara/Core/ObjectHandle.hpp>
#include <algorithm> #include <algorithm>
#include <cassert>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
@ -35,10 +36,10 @@ namespace Nz
*/ */
template<typename T> template<typename T>
HandledObject<T>::HandledObject(HandledObject&& object) noexcept : HandledObject<T>::HandledObject(HandledObject&& object) noexcept :
m_handles(std::move(object.m_handles)) m_handleData(std::move(object.m_handleData))
{ {
for (ObjectHandle<T>* handle : m_handles) if (m_handleData)
handle->OnObjectMoved(static_cast<T*>(this)); m_handleData->object = static_cast<T*>(this);
} }
/*! /*!
@ -88,73 +89,42 @@ namespace Nz
{ {
UnregisterAllHandles(); UnregisterAllHandles();
m_handles = std::move(object.m_handles); m_handleData = std::move(object.m_handleData);
for (ObjectHandle<T>* handle : m_handles)
handle->OnObjectMoved(static_cast<T*>(this)); if (m_handleData)
m_handleData->object = static_cast<T*>(this);
return *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<typename T>
void HandledObject<T>::RegisterHandle(ObjectHandle<T>* handle)
{
m_handles.push_back(handle);
}
/*! /*!
* \brief Unregisters all handles * \brief Unregisters all handles
*/ */
template<typename T> template<typename T>
void HandledObject<T>::UnregisterAllHandles() noexcept void HandledObject<T>::UnregisterAllHandles() noexcept
{ {
// Tell every handle we got destroyed, to null them if (m_handleData)
for (ObjectHandle<T>* handle : m_handles) {
handle->OnObjectDestroyed(); m_handleData->object = nullptr;
m_handleData.reset();
m_handles.clear(); }
} }
/*!
* \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<typename T> template<typename T>
void HandledObject<T>::UnregisterHandle(ObjectHandle<T>* handle) noexcept std::shared_ptr<const Detail::HandleData> HandledObject<T>::GetHandleData()
{ {
auto it = std::find(m_handles.begin(), m_handles.end(), handle); if (!m_handleData)
NazaraAssert(it != m_handles.end(), "Handle not registered"); InitHandleData();
// Swap and pop idiom, more efficient than vector::erase return std::shared_ptr<const Detail::HandleData>(m_handleData);
std::swap(*it, m_handles.back());
m_handles.pop_back();
} }
/*!
* \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<typename T> template<typename T>
void HandledObject<T>::UpdateHandle(ObjectHandle<T>* oldHandle, ObjectHandle<T>* newHandle) noexcept void HandledObject<T>::InitHandleData()
{ {
auto it = std::find(m_handles.begin(), m_handles.end(), oldHandle); assert(!m_handleData);
NazaraAssert(it != m_handles.end(), "Handle not registered");
// Simply update the handle m_handleData = std::make_shared<Detail::HandleData>();
*it = newHandle; m_handleData->object = static_cast<T*>(this);
} }
} }

View File

@ -8,12 +8,12 @@
#define NAZARA_OBJECTHANDLE_HPP #define NAZARA_OBJECTHANDLE_HPP
#include <Nazara/Core/Algorithm.hpp> #include <Nazara/Core/Algorithm.hpp>
#include <Nazara/Core/HandledObject.hpp>
#include <memory>
#include <ostream> #include <ostream>
namespace Nz namespace Nz
{ {
template<typename T> class HandledObject;
template<typename T> template<typename T>
class ObjectHandle class ObjectHandle
{ {
@ -22,7 +22,7 @@ namespace Nz
public: public:
ObjectHandle(); ObjectHandle();
explicit ObjectHandle(T* object); explicit ObjectHandle(T* object);
ObjectHandle(const ObjectHandle& handle); ObjectHandle(const ObjectHandle& handle) = default;
ObjectHandle(ObjectHandle&& handle) noexcept; ObjectHandle(ObjectHandle&& handle) noexcept;
~ObjectHandle(); ~ObjectHandle();
@ -43,16 +43,13 @@ namespace Nz
T* operator->() const; T* operator->() const;
ObjectHandle& operator=(T* object); ObjectHandle& operator=(T* object);
ObjectHandle& operator=(const ObjectHandle& handle); ObjectHandle& operator=(const ObjectHandle& handle) = default;
ObjectHandle& operator=(ObjectHandle&& handle) noexcept; ObjectHandle& operator=(ObjectHandle&& handle) noexcept;
static const ObjectHandle InvalidHandle; static const ObjectHandle InvalidHandle;
protected: protected:
void OnObjectDestroyed() noexcept; std::shared_ptr<const Detail::HandleData> m_handleData;
void OnObjectMoved(T* newObject) noexcept;
T* m_object;
}; };
template<typename T> std::ostream& operator<<(std::ostream& out, const ObjectHandle<T>& handle); template<typename T> std::ostream& operator<<(std::ostream& out, const ObjectHandle<T>& handle);

View File

@ -15,37 +15,15 @@ namespace Nz
* \brief Core class that represents a object handle * \brief Core class that represents a object handle
*/ */
/*!
* \brief Constructs a ObjectHandle object by default
*/
template<typename T>
ObjectHandle<T>::ObjectHandle() :
m_object(nullptr)
{
}
/*! /*!
* \brief Constructs a ObjectHandle object with a pointer to an object * \brief Constructs a ObjectHandle object with a pointer to an object
* *
* \param object Pointer to handle like an object (can be nullptr) * \param object Pointer to handle like an object (can be nullptr)
*/ */
template<typename T> template<typename T>
ObjectHandle<T>::ObjectHandle(T* object) : ObjectHandle<T>::ObjectHandle() :
ObjectHandle() m_handleData(Detail::HandleData::GetEmptyObject())
{ {
Reset(object);
}
/*!
* \brief Constructs a ObjectHandle object by assignation
*
* \param handle ObjectHandle to assign into this
*/
template<typename T>
ObjectHandle<T>::ObjectHandle(const ObjectHandle& handle) :
ObjectHandle()
{
Reset(handle);
} }
/*! /*!
@ -54,12 +32,22 @@ namespace Nz
* \param handle ObjectHandle to move into this * \param handle ObjectHandle to move into this
*/ */
template<typename T> template<typename T>
ObjectHandle<T>::ObjectHandle(ObjectHandle&& handle) noexcept : ObjectHandle<T>::ObjectHandle(ObjectHandle&& handle) noexcept
ObjectHandle()
{ {
Reset(std::move(handle)); 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<typename T>
ObjectHandle<T>::ObjectHandle(T* object)
{
Reset(object);
}
/*! /*!
* \brief Destructs the object and calls reset with nullptr * \brief Destructs the object and calls reset with nullptr
* *
@ -78,7 +66,7 @@ namespace Nz
template<typename T> template<typename T>
T* ObjectHandle<T>::GetObject() const T* ObjectHandle<T>::GetObject() const
{ {
return m_object; return static_cast<T*>(m_handleData->object);
} }
/*! /*!
@ -88,7 +76,7 @@ namespace Nz
template<typename T> template<typename T>
bool ObjectHandle<T>::IsValid() const bool ObjectHandle<T>::IsValid() const
{ {
return m_object != nullptr; return m_handleData->object != nullptr;
} }
/*! /*!
@ -99,14 +87,10 @@ namespace Nz
template<typename T> template<typename T>
void ObjectHandle<T>::Reset(T* object) void ObjectHandle<T>::Reset(T* object)
{ {
// If we already have an entity, we must alert it that we are not pointing to it anymore if (object)
if (m_object) m_handleData = object->GetHandleData();
m_object->UnregisterHandle(this); else
m_handleData = Detail::HandleData::GetEmptyObject();
m_object = object;
if (m_object)
// We alert the new entity that we are pointing to it
m_object->RegisterHandle(this);
} }
/*! /*!
@ -117,7 +101,7 @@ namespace Nz
template<typename T> template<typename T>
void ObjectHandle<T>::Reset(const ObjectHandle& handle) void ObjectHandle<T>::Reset(const ObjectHandle& handle)
{ {
Reset(handle.GetObject()); m_handleData = handle.m_handleData;
} }
/*! /*!
@ -128,20 +112,8 @@ namespace Nz
template<typename T> template<typename T>
void ObjectHandle<T>::Reset(ObjectHandle&& handle) noexcept void ObjectHandle<T>::Reset(ObjectHandle&& handle) noexcept
{ {
if (this == &handle) m_handleData = std::move(handle.m_handleData);
return; handle.m_handleData = Detail::HandleData::GetEmptyObject();
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;
} }
/*! /*!
@ -153,23 +125,8 @@ namespace Nz
template<typename T> template<typename T>
ObjectHandle<T>& ObjectHandle<T>::Swap(ObjectHandle& handle) ObjectHandle<T>& ObjectHandle<T>::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 // We do the swap
std::swap(m_object, handle.m_object); std::swap(m_handleData, handle.m_handleData);
return *this; return *this;
} }
@ -183,7 +140,7 @@ namespace Nz
Nz::StringStream ss; Nz::StringStream ss;
ss << "ObjectHandle("; ss << "ObjectHandle(";
if (IsValid()) if (IsValid())
ss << m_object->ToString(); ss << GetObject()->ToString();
else else
ss << "Null"; ss << "Null";
@ -211,7 +168,7 @@ namespace Nz
template<typename T> template<typename T>
ObjectHandle<T>::operator T*() const ObjectHandle<T>::operator T*() const
{ {
return m_object; return GetObject();
} }
/*! /*!
@ -221,7 +178,7 @@ namespace Nz
template<typename T> template<typename T>
T* ObjectHandle<T>::operator->() const T* ObjectHandle<T>::operator->() const
{ {
return m_object; return GetObject();
} }
/*! /*!
@ -231,23 +188,9 @@ namespace Nz
* \param entity Pointer to handle like an object (can be nullptr) * \param entity Pointer to handle like an object (can be nullptr)
*/ */
template<typename T> template<typename T>
ObjectHandle<T>& ObjectHandle<T>::operator=(T* entity) ObjectHandle<T>& ObjectHandle<T>::operator=(T* object)
{ {
Reset(entity); Reset(object);
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<typename T>
ObjectHandle<T>& ObjectHandle<T>::operator=(const ObjectHandle& handle)
{
Reset(handle);
return *this; return *this;
} }
@ -266,26 +209,6 @@ namespace Nz
return *this; return *this;
} }
/*!
* \brief Action to do on object destruction
*/
template<typename T>
void ObjectHandle<T>::OnObjectDestroyed() noexcept
{
// Shortcut
m_object = nullptr;
}
/*!
* \brief Action to do on object move
*/
template<typename T>
void ObjectHandle<T>::OnObjectMoved(T* newObject) noexcept
{
// The object has been moved, update our pointer
m_object = newObject;
}
/*! /*!
* \brief Output operator * \brief Output operator
* \return The stream * \return The stream
@ -388,7 +311,7 @@ namespace Nz
template<typename T> template<typename T>
bool operator<(const ObjectHandle<T>& lhs, const ObjectHandle<T>& rhs) bool operator<(const ObjectHandle<T>& lhs, const ObjectHandle<T>& rhs)
{ {
return lhs.m_object < rhs.m_object; return lhs.GetObject() < rhs.GetObject();
} }
/*! /*!
@ -401,7 +324,7 @@ namespace Nz
template<typename T> template<typename T>
bool operator<(const T& lhs, const ObjectHandle<T>& rhs) bool operator<(const T& lhs, const ObjectHandle<T>& rhs)
{ {
return &lhs < rhs.m_object; return &lhs < rhs.GetObject();
} }
/*! /*!
@ -414,7 +337,7 @@ namespace Nz
template<typename T> template<typename T>
bool operator<(const ObjectHandle<T>& lhs, const T& rhs) bool operator<(const ObjectHandle<T>& lhs, const T& rhs)
{ {
return lhs.m_object < &rhs; return lhs.GetObject() < &rhs;
} }
/*! /*!

View File

@ -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 <Nazara/Core/HandledObject.hpp>
#include <Nazara/Core/Debug.hpp>
namespace Nz
{
namespace Detail
{
std::shared_ptr<HandleData> HandleData::GetEmptyObject()
{
static std::shared_ptr<HandleData> emptyHandleData = std::make_shared<HandleData>(HandleData{});
return emptyHandleData;
}
}
}