mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-20 17:27:36 +01:00
Core/Utils: Added a custom smart pointer type unique_trackable_ptr - a specialized variant of std::shared_ptr that enforces unique ownership
* This is intended to be used by external code unable to track object lifetime such as custom scripting engines
(cherry picked from commit 32e54b6bd1)
This commit is contained in:
274
src/common/Utilities/UniqueTrackablePtr.h
Normal file
274
src/common/Utilities/UniqueTrackablePtr.h
Normal file
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TRINITYCORE_UNIQUE_TRACKING_PTR_H
|
||||
#define TRINITYCORE_UNIQUE_TRACKING_PTR_H
|
||||
|
||||
#include "Define.h"
|
||||
#include <memory>
|
||||
|
||||
namespace Trinity
|
||||
{
|
||||
template <typename T, typename Deleter = std::default_delete<T>>
|
||||
class unique_trackable_ptr;
|
||||
|
||||
template <typename T>
|
||||
class unique_weak_ptr;
|
||||
|
||||
template <typename T>
|
||||
class unique_strong_ref_ptr;
|
||||
|
||||
/**
|
||||
* \brief Specialized variant of std::shared_ptr that enforces unique ownership and/or std::unique_ptr with std::weak_ptr capabilities
|
||||
* Implementation has the same overhead as a std::shared_ptr, that is, a separate allocation for control block that holds use counters
|
||||
* \tparam T Type of held object
|
||||
* \tparam Deleter Object deleter (defaults to std::default_delete<T>)
|
||||
*/
|
||||
template <typename T, typename Deleter>
|
||||
class unique_trackable_ptr
|
||||
{
|
||||
public:
|
||||
using element_type = T;
|
||||
using pointer = T*;
|
||||
using deleter_type = Deleter;
|
||||
|
||||
unique_trackable_ptr() : _ptr(nullptr, deleter_type()) { }
|
||||
|
||||
explicit unique_trackable_ptr(pointer ptr) : _ptr(ptr, deleter_type()) { }
|
||||
|
||||
explicit unique_trackable_ptr(pointer ptr, deleter_type deleter) : _ptr(ptr, std::move(deleter)) { }
|
||||
|
||||
unique_trackable_ptr(unique_trackable_ptr const&) = delete;
|
||||
|
||||
unique_trackable_ptr(unique_trackable_ptr&& other) noexcept
|
||||
: _ptr(std::move(other._ptr)) { }
|
||||
|
||||
unique_trackable_ptr& operator=(unique_trackable_ptr const&) = delete;
|
||||
|
||||
unique_trackable_ptr& operator=(unique_trackable_ptr&& other) noexcept
|
||||
{
|
||||
_ptr = std::move(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~unique_trackable_ptr() = default;
|
||||
|
||||
unique_trackable_ptr& operator=(std::nullptr_t)
|
||||
{
|
||||
reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(unique_trackable_ptr& other) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
swap(_ptr, other._ptr);
|
||||
}
|
||||
|
||||
element_type& operator*() const
|
||||
{
|
||||
return *_ptr;
|
||||
}
|
||||
|
||||
pointer operator->() const
|
||||
{
|
||||
return _ptr.operator->();
|
||||
}
|
||||
|
||||
pointer get() const
|
||||
{
|
||||
return _ptr.get();
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return static_cast<bool>(_ptr);
|
||||
}
|
||||
|
||||
void reset(pointer ptr = nullptr, deleter_type deleter = {})
|
||||
{
|
||||
_ptr.reset(ptr, std::move(deleter));
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T0>
|
||||
friend class unique_weak_ptr;
|
||||
|
||||
template <typename T0, typename... Args>
|
||||
friend std::enable_if_t<!std::is_array_v<T0>, unique_trackable_ptr<T0>> make_unique_trackable(Args&&... args);
|
||||
|
||||
template <typename T0>
|
||||
friend std::enable_if_t<std::is_unbounded_array_v<T0>, unique_trackable_ptr<T0>> make_unique_trackable(std::size_t N);
|
||||
|
||||
template <typename T0>
|
||||
friend std::enable_if_t<std::is_unbounded_array_v<T0>, unique_trackable_ptr<T0>> make_unique_trackable(std::size_t N, std::remove_extent_t<T0> const& val);
|
||||
|
||||
template <typename T0>
|
||||
friend std::enable_if_t<std::is_bounded_array_v<T0>, unique_trackable_ptr<T0>> make_unique_trackable();
|
||||
|
||||
template <typename T0>
|
||||
friend std::enable_if_t<std::is_bounded_array_v<T0>, unique_trackable_ptr<T0>> make_unique_trackable(std::remove_extent_t<T0> const& val);
|
||||
|
||||
std::shared_ptr<element_type> _ptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Trinity::unique_trackable_ptr companion class, replicating what std::weak_ptr is to std::shared_ptr
|
||||
* \tparam T Type of held object
|
||||
*/
|
||||
template <typename T>
|
||||
class unique_weak_ptr
|
||||
{
|
||||
public:
|
||||
using element_type = T;
|
||||
using pointer = T*;
|
||||
|
||||
unique_weak_ptr() = default;
|
||||
|
||||
template<typename Deleter>
|
||||
unique_weak_ptr(unique_trackable_ptr<T, Deleter> const& trackable) : _ptr(trackable._ptr)
|
||||
{
|
||||
}
|
||||
|
||||
unique_weak_ptr(unique_weak_ptr const& other) = default;
|
||||
unique_weak_ptr(unique_weak_ptr&& other) noexcept = default;
|
||||
|
||||
template<typename Deleter>
|
||||
unique_weak_ptr& operator=(unique_trackable_ptr<T, Deleter> const& trackable)
|
||||
{
|
||||
_ptr = trackable._ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
unique_weak_ptr& operator=(unique_weak_ptr const& other) = default;
|
||||
unique_weak_ptr& operator=(unique_weak_ptr&& other) noexcept = default;
|
||||
|
||||
~unique_weak_ptr() = default;
|
||||
|
||||
void swap(unique_weak_ptr& other) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
swap(_ptr, other._ptr);
|
||||
}
|
||||
|
||||
bool expired() const
|
||||
{
|
||||
return _ptr.expired();
|
||||
}
|
||||
|
||||
unique_strong_ref_ptr<element_type> lock() const
|
||||
{
|
||||
return unique_strong_ref_ptr<element_type>(_ptr.lock());
|
||||
}
|
||||
|
||||
private:
|
||||
std::weak_ptr<element_type> _ptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Result of unique_weak_ptr::lock() function, this class holds a temporary strong reference to held object
|
||||
* to prevent it from being deallocated by another thread while it is being actively accessed.
|
||||
* This class is non-movable and non-copypable and is intended only for short lived local variables
|
||||
* \tparam T Type of held object
|
||||
*/
|
||||
template <typename T>
|
||||
class unique_strong_ref_ptr
|
||||
{
|
||||
public:
|
||||
using element_type = T;
|
||||
using pointer = T*;
|
||||
|
||||
unique_strong_ref_ptr(unique_strong_ref_ptr const&) = delete;
|
||||
unique_strong_ref_ptr(unique_strong_ref_ptr&&) = delete;
|
||||
unique_strong_ref_ptr& operator=(unique_strong_ref_ptr const&) = delete;
|
||||
unique_strong_ref_ptr& operator=(unique_strong_ref_ptr&&) = delete;
|
||||
|
||||
~unique_strong_ref_ptr() = default;
|
||||
|
||||
element_type& operator*() const
|
||||
{
|
||||
return *_ptr;
|
||||
}
|
||||
|
||||
pointer operator->() const
|
||||
{
|
||||
return _ptr.operator->();
|
||||
}
|
||||
|
||||
pointer get() const
|
||||
{
|
||||
return _ptr.get();
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return static_cast<bool>(_ptr);
|
||||
}
|
||||
|
||||
friend std::strong_ordering operator<=>(unique_strong_ref_ptr const&, unique_strong_ref_ptr const&) = default;
|
||||
|
||||
private:
|
||||
template <typename T0>
|
||||
friend class unique_weak_ptr;
|
||||
|
||||
unique_strong_ref_ptr(std::shared_ptr<element_type> ptr) : _ptr(std::move(ptr)) { }
|
||||
|
||||
std::shared_ptr<element_type> _ptr;
|
||||
};
|
||||
|
||||
template <typename T, typename... Args>
|
||||
std::enable_if_t<!std::is_array_v<T>, unique_trackable_ptr<T>> make_unique_trackable(Args&&... args)
|
||||
{
|
||||
unique_trackable_ptr<T> ptr;
|
||||
ptr._ptr = std::make_shared<T>(std::forward<Args>(args)...);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_unbounded_array_v<T>, unique_trackable_ptr<T>> make_unique_trackable(std::size_t N)
|
||||
{
|
||||
unique_trackable_ptr<T> ptr;
|
||||
ptr._ptr = std::make_shared<T>(N);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_unbounded_array_v<T>, unique_trackable_ptr<T>> make_unique_trackable(std::size_t N, std::remove_extent_t<T> const& val)
|
||||
{
|
||||
unique_trackable_ptr<T> ptr;
|
||||
ptr._ptr = std::make_shared<T>(N, val);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_bounded_array_v<T>, unique_trackable_ptr<T>> make_unique_trackable()
|
||||
{
|
||||
unique_trackable_ptr<T> ptr;
|
||||
ptr._ptr = std::make_shared<T>();
|
||||
return ptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_bounded_array_v<T>, unique_trackable_ptr<T>> make_unique_trackable(std::remove_extent_t<T> const& val)
|
||||
{
|
||||
unique_trackable_ptr<T> ptr;
|
||||
ptr._ptr = std::make_shared<T>(val);
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // TRINITYCORE_UNIQUE_TRACKING_PTR_H
|
||||
90
tests/common/UniqueTrackablePtr.cpp
Normal file
90
tests/common/UniqueTrackablePtr.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "tc_catch2.h"
|
||||
|
||||
#include "UniqueTrackingPtr.h"
|
||||
|
||||
struct TestObj
|
||||
{
|
||||
TestObj(bool* deleted = nullptr) : Deleted(deleted) { }
|
||||
|
||||
~TestObj()
|
||||
{
|
||||
if (Deleted)
|
||||
*Deleted = true;
|
||||
}
|
||||
|
||||
bool* Deleted = nullptr;
|
||||
};
|
||||
|
||||
TEST_CASE("Trinity::unique_trackable_ptr frees memory", "[UniqueTrackingPtr]")
|
||||
{
|
||||
bool deleted = false;
|
||||
|
||||
SECTION("reassigning new object deletes old one")
|
||||
{
|
||||
Trinity::unique_trackable_ptr<TestObj> ptr = Trinity::make_unique_trackable<TestObj>(&deleted);
|
||||
|
||||
ptr.reset(new TestObj());
|
||||
|
||||
REQUIRE(deleted == true);
|
||||
}
|
||||
|
||||
SECTION("going out of scope deletes object")
|
||||
{
|
||||
REQUIRE(deleted == false);
|
||||
|
||||
{
|
||||
Trinity::unique_trackable_ptr<TestObj> ptr = Trinity::make_unique_trackable<TestObj>(&deleted);
|
||||
}
|
||||
|
||||
REQUIRE(deleted == true);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Trinity::unique_weak_ptr", "[UniqueTrackingPtr]")
|
||||
{
|
||||
Trinity::unique_trackable_ptr<int> ptr = Trinity::make_unique_trackable<int>();
|
||||
|
||||
Trinity::unique_weak_ptr<int> weakRef = ptr;
|
||||
|
||||
SECTION("when unique_trackable_ptr no longer holds a value then weak cannot retrieve it")
|
||||
{
|
||||
ptr.reset();
|
||||
|
||||
REQUIRE(weakRef.expired());
|
||||
REQUIRE(!weakRef.lock());
|
||||
}
|
||||
|
||||
SECTION("when unique_trackable_ptr is reassigned then weak cannot retrieve old value")
|
||||
{
|
||||
ptr.reset(new int);
|
||||
|
||||
Trinity::unique_weak_ptr<int> weakRef2 = ptr;
|
||||
|
||||
REQUIRE(weakRef.expired());
|
||||
REQUIRE(!weakRef2.expired());
|
||||
REQUIRE(weakRef.lock() != weakRef2.lock());
|
||||
}
|
||||
|
||||
SECTION("when unique_trackable_ptr holds a value then weak can retrieve it")
|
||||
{
|
||||
REQUIRE(!weakRef.expired());
|
||||
REQUIRE(!!weakRef.lock());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user