Core/ObjectHandle: Remade object handle system
This commit is contained in:
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
Reference in New Issue
Block a user