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
- ⚠️ 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:

View File

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

View File

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

View File

@ -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 <NDK/EntityOwner.hpp>
#include <Nazara/Core/StringStream.hpp>
#include <functional>
#include <limits>
@ -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();
}
/*!

View File

@ -13,6 +13,16 @@
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>
@ -35,11 +45,10 @@ namespace Nz
void UnregisterAllHandles() noexcept;
private:
void RegisterHandle(ObjectHandle<T>* handle);
void UnregisterHandle(ObjectHandle<T>* handle) noexcept;
void UpdateHandle(ObjectHandle<T>* oldHandle, ObjectHandle<T>* newHandle) noexcept;
std::shared_ptr<const Detail::HandleData> GetHandleData();
void InitHandleData();
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"
// 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/ObjectHandle.hpp>
#include <algorithm>
#include <cassert>
#include <type_traits>
#include <utility>
@ -35,10 +36,10 @@ namespace Nz
*/
template<typename T>
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)
handle->OnObjectMoved(static_cast<T*>(this));
if (m_handleData)
m_handleData->object = static_cast<T*>(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<T>* handle : m_handles)
handle->OnObjectMoved(static_cast<T*>(this));
m_handleData = std::move(object.m_handleData);
if (m_handleData)
m_handleData->object = static_cast<T*>(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
*/
template<typename T>
void HandledObject<T>::UnregisterAllHandles() noexcept
{
// Tell every handle we got destroyed, to null them
for (ObjectHandle<T>* 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<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);
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<const Detail::HandleData>(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<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);
NazaraAssert(it != m_handles.end(), "Handle not registered");
assert(!m_handleData);
// Simply update the handle
*it = newHandle;
m_handleData = std::make_shared<Detail::HandleData>();
m_handleData->object = static_cast<T*>(this);
}
}

View File

@ -8,12 +8,12 @@
#define NAZARA_OBJECTHANDLE_HPP
#include <Nazara/Core/Algorithm.hpp>
#include <Nazara/Core/HandledObject.hpp>
#include <memory>
#include <ostream>
namespace Nz
{
template<typename T> class HandledObject;
template<typename T>
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<const Detail::HandleData> m_handleData;
};
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 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
*
* \param object Pointer to handle like an object (can be nullptr)
*/
template<typename T>
ObjectHandle<T>::ObjectHandle(T* object) :
ObjectHandle()
ObjectHandle<T>::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
*/
template<typename T>
ObjectHandle<T>::ObjectHandle(ObjectHandle&& handle) noexcept :
ObjectHandle()
ObjectHandle<T>::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<typename T>
ObjectHandle<T>::ObjectHandle(T* object)
{
Reset(object);
}
/*!
* \brief Destructs the object and calls reset with nullptr
*
@ -78,7 +66,7 @@ namespace Nz
template<typename T>
T* ObjectHandle<T>::GetObject() const
{
return m_object;
return static_cast<T*>(m_handleData->object);
}
/*!
@ -88,7 +76,7 @@ namespace Nz
template<typename T>
bool ObjectHandle<T>::IsValid() const
{
return m_object != nullptr;
return m_handleData->object != nullptr;
}
/*!
@ -99,14 +87,10 @@ namespace Nz
template<typename T>
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 (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<typename T>
void ObjectHandle<T>::Reset(const ObjectHandle& handle)
{
Reset(handle.GetObject());
m_handleData = handle.m_handleData;
}
/*!
@ -128,20 +112,8 @@ namespace Nz
template<typename T>
void ObjectHandle<T>::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<typename T>
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
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<typename T>
ObjectHandle<T>::operator T*() const
{
return m_object;
return GetObject();
}
/*!
@ -221,7 +178,7 @@ namespace Nz
template<typename T>
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)
*/
template<typename T>
ObjectHandle<T>& ObjectHandle<T>::operator=(T* entity)
ObjectHandle<T>& ObjectHandle<T>::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<typename T>
ObjectHandle<T>& ObjectHandle<T>::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<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
* \return The stream
@ -388,7 +311,7 @@ namespace Nz
template<typename T>
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>
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>
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;
}
}
}