diff options
-rw-r--r-- | src/common/Utilities/UniqueTrackablePtr.h | 274 | ||||
-rw-r--r-- | tests/common/UniqueTrackablePtr.cpp | 90 |
2 files changed, 364 insertions, 0 deletions
diff --git a/src/common/Utilities/UniqueTrackablePtr.h b/src/common/Utilities/UniqueTrackablePtr.h new file mode 100644 index 00000000000..0a09ead1eed --- /dev/null +++ b/src/common/Utilities/UniqueTrackablePtr.h @@ -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 diff --git a/tests/common/UniqueTrackablePtr.cpp b/tests/common/UniqueTrackablePtr.cpp new file mode 100644 index 00000000000..6a1db9fa277 --- /dev/null +++ b/tests/common/UniqueTrackablePtr.cpp @@ -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()); + } +} |