From ab800ddd36b54665618e3066671307f27f2a1e23 Mon Sep 17 00:00:00 2001 From: Shauren Date: Fri, 12 Jul 2024 19:38:25 +0200 Subject: [PATCH] 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 --- src/shared/CMakeLists.txt | 1 + src/shared/Util/UniqueTrackablePtr.h | 478 +++++++++++++++++++++++++++ 2 files changed, 479 insertions(+) create mode 100644 src/shared/Util/UniqueTrackablePtr.h diff --git a/src/shared/CMakeLists.txt b/src/shared/CMakeLists.txt index 5058c8c6c17..2a96a2b40f0 100644 --- a/src/shared/CMakeLists.txt +++ b/src/shared/CMakeLists.txt @@ -134,6 +134,7 @@ set(SRC_GRP_UTIL Util/Util.h Util/ProducerConsumerQueue.h Util/CommonDefines.h + Util/UniqueTrackablePtr.h ) set(LIBRARY_SRCS diff --git a/src/shared/Util/UniqueTrackablePtr.h b/src/shared/Util/UniqueTrackablePtr.h new file mode 100644 index 00000000000..843d08a03a5 --- /dev/null +++ b/src/shared/Util/UniqueTrackablePtr.h @@ -0,0 +1,478 @@ +/* + * 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 . + */ + +#ifndef MANGOS_UNIQUE_TRACKABLE_PTR_H +#define MANGOS_UNIQUE_TRACKABLE_PTR_H + +#include + +namespace MaNGOS +{ +// C++20 utilities +template +inline constexpr bool is_bounded_array_v = std::is_array_v && std::extent_v != 0; + +template +inline constexpr bool is_unbounded_array_v = std::is_array_v && std::extent_v == 0; + +template +class unique_trackable_ptr; + +template +class unique_weak_ptr; + +template +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 + */ +template +class unique_trackable_ptr +{ +public: + using element_type = T; + using pointer = T*; + + unique_trackable_ptr() : _ptr() { } + + explicit unique_trackable_ptr(pointer ptr) + : _ptr(ptr) { } + + template , std::is_invocable>, int> = 0> + explicit unique_trackable_ptr(pointer ptr, Deleter 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)) { } + + template , int> = 0> + 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._ptr); + return *this; + } + + template , int> = 0> + unique_trackable_ptr& operator=(unique_trackable_ptr&& other) noexcept + { + _ptr = std::move(other)._ptr; + 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(_ptr); + } + + void reset() + { + _ptr.reset(); + } + + void reset(pointer ptr) + { + _ptr.reset(ptr); + } + + template , std::is_invocable>, int> = 0> + void reset(pointer ptr, Deleter deleter) + { + _ptr.reset(ptr, std::move(deleter)); + } + +private: + template + friend class unique_trackable_ptr; + + template + friend class unique_weak_ptr; + + template + friend std::enable_if_t, unique_trackable_ptr> make_unique_trackable(Args&&... args); + + template + friend std::enable_if_t, unique_trackable_ptr> make_unique_trackable(std::size_t N); + + template + friend std::enable_if_t, unique_trackable_ptr> make_unique_trackable(std::size_t N, std::remove_extent_t const& val); + + template + friend std::enable_if_t, unique_trackable_ptr> make_unique_trackable(); + + template + friend std::enable_if_t, unique_trackable_ptr> make_unique_trackable(std::remove_extent_t const& val); + + std::shared_ptr _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 +class unique_weak_ptr +{ +public: + using element_type = T; + using pointer = T*; + + unique_weak_ptr() = default; + + unique_weak_ptr(unique_trackable_ptr const& trackable) + : _ptr(trackable._ptr) { } + + unique_weak_ptr(unique_weak_ptr const& other) = default; + + template , int> = 0> + unique_weak_ptr(unique_weak_ptr const& other) noexcept + : _ptr(other._ptr) { } + + unique_weak_ptr(unique_weak_ptr&& other) noexcept = default; + + template , int> = 0> + unique_weak_ptr(unique_weak_ptr&& other) noexcept + : _ptr(std::move(other)._ptr) { } + + unique_weak_ptr& operator=(unique_trackable_ptr const& trackable) + { + _ptr = trackable._ptr; + return *this; + } + + unique_weak_ptr& operator=(unique_weak_ptr const& other) = default; + + template , int> = 0> + unique_weak_ptr& operator=(unique_weak_ptr&& other) + { + _ptr = std::move(other)._ptr; + return *this; + } + + 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 lock() const + { + return unique_strong_ref_ptr(_ptr.lock()); + } + +private: + template + friend class unique_weak_ptr; + + template + friend class unique_strong_ref_ptr; + + template + friend unique_weak_ptr static_pointer_cast(unique_weak_ptr const& other); + + template + friend unique_weak_ptr const_pointer_cast(unique_weak_ptr const& other); + + template + friend unique_weak_ptr reinterpret_pointer_cast(unique_weak_ptr const& other); + + template + friend unique_weak_ptr dynamic_pointer_cast(unique_weak_ptr const& other); + + std::weak_ptr _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 +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(_ptr); + } + + operator unique_weak_ptr() const + { + unique_weak_ptr weak; + weak._ptr = _ptr; + return weak; + } + +private: + template + friend class unique_weak_ptr; + + template + friend unique_strong_ref_ptr static_pointer_cast(unique_strong_ref_ptr const& other); + + template + friend unique_strong_ref_ptr static_pointer_cast(unique_strong_ref_ptr&& other); + + template + friend unique_strong_ref_ptr const_pointer_cast(unique_strong_ref_ptr const& other); + + template + friend unique_strong_ref_ptr const_pointer_cast(unique_strong_ref_ptr&& other); + + template + friend unique_strong_ref_ptr reinterpret_pointer_cast(unique_strong_ref_ptr const& other); + + template + friend unique_strong_ref_ptr reinterpret_pointer_cast(unique_strong_ref_ptr&& other); + + template + friend unique_strong_ref_ptr dynamic_pointer_cast(unique_strong_ref_ptr const& other); + + template + friend unique_strong_ref_ptr dynamic_pointer_cast(unique_strong_ref_ptr&& other); + + unique_strong_ref_ptr(std::shared_ptr ptr) : _ptr(std::move(ptr)) { } + + std::shared_ptr _ptr; +}; + +// Ugly macros that will become unneccessary with C++20 operator<=> +#define UNIQUE_TRACKABLE_COMPARISON_OPERATOR(cls, op) \ +template \ +bool operator op (cls const& left, cls const& right) { return left.get() op right.get(); } + +#define UNIQUE_TRACKABLE_COMPARISON_OPERATOR_NULLPTR(cls, op) \ +template \ +bool operator op (cls const& left, std::nullptr_t) { return left.get() op static_cast::element_type*>(nullptr); }\ +template \ +bool operator op (std::nullptr_t, cls const& right) { return static_cast::element_type*>(nullptr) op right.get(); } + +#define UNIQUE_TRACKABLE_COMPARISON_OPERATORS(STAMPER, cls) \ + STAMPER(cls, ==) \ + STAMPER(cls, !=) \ + STAMPER(cls, <) \ + STAMPER(cls, >=) \ + STAMPER(cls, >) \ + STAMPER(cls, <=) + +#define UNIQUE_TRACKABLE_COMPARISON_OPERATORS_FOR_CLASS(cls) \ + UNIQUE_TRACKABLE_COMPARISON_OPERATORS(UNIQUE_TRACKABLE_COMPARISON_OPERATOR, cls) \ + UNIQUE_TRACKABLE_COMPARISON_OPERATORS(UNIQUE_TRACKABLE_COMPARISON_OPERATOR_NULLPTR, cls) + +// unique_trackable_ptr funcions +UNIQUE_TRACKABLE_COMPARISON_OPERATORS_FOR_CLASS(unique_trackable_ptr) + +template +std::enable_if_t, unique_trackable_ptr> make_unique_trackable(Args&&... args) +{ + unique_trackable_ptr ptr; + ptr._ptr = std::make_shared(std::forward(args)...); + return ptr; +} + +template +std::enable_if_t, unique_trackable_ptr> make_unique_trackable(std::size_t N) +{ + unique_trackable_ptr ptr; + ptr._ptr = std::make_shared(N); + return ptr; +} + +template +std::enable_if_t, unique_trackable_ptr> make_unique_trackable(std::size_t N, std::remove_extent_t const& val) +{ + unique_trackable_ptr ptr; + ptr._ptr = std::make_shared(N, val); + return ptr; +} + +template +std::enable_if_t, unique_trackable_ptr> make_unique_trackable() +{ + unique_trackable_ptr ptr; + ptr._ptr = std::make_shared(); + return ptr; +} + +template +std::enable_if_t, unique_trackable_ptr> make_unique_trackable(std::remove_extent_t const& val) +{ + unique_trackable_ptr ptr; + ptr._ptr = std::make_shared(val); + return ptr; +} + +// unique_weak_ptr funcions + +template +unique_weak_ptr static_pointer_cast(unique_weak_ptr const& other) +{ + unique_weak_ptr to; + to._ptr = std::static_pointer_cast(other._ptr.lock()); + return to; +} + +template +unique_weak_ptr const_pointer_cast(unique_weak_ptr const& other) +{ + unique_weak_ptr to; + to._ptr = std::const_pointer_cast(other._ptr.lock()); + return to; +} + +template +unique_weak_ptr reinterpret_pointer_cast(unique_weak_ptr const& other) +{ + unique_weak_ptr to; + to._ptr = std::reinterpret_pointer_cast(other._ptr.lock()); + return to; +} + +template +unique_weak_ptr dynamic_pointer_cast(unique_weak_ptr const& other) +{ + unique_weak_ptr to; + to._ptr = std::dynamic_pointer_cast(other._ptr.lock()); + return to; +} + +// unique_strong_ref_ptr funcions +UNIQUE_TRACKABLE_COMPARISON_OPERATORS_FOR_CLASS(unique_strong_ref_ptr) + +template +unique_strong_ref_ptr static_pointer_cast(unique_strong_ref_ptr const& other) +{ + return unique_strong_ref_ptr(std::static_pointer_cast(other._ptr)); +} + +template +unique_strong_ref_ptr static_pointer_cast(unique_strong_ref_ptr&& other) +{ + return unique_strong_ref_ptr(std::static_pointer_cast(std::move(other._ptr))); +} + +template +unique_strong_ref_ptr const_pointer_cast(unique_strong_ref_ptr const& other) +{ + return unique_strong_ref_ptr(std::const_pointer_cast(other._ptr)); +} + +template +unique_strong_ref_ptr const_pointer_cast(unique_strong_ref_ptr&& other) +{ + return unique_strong_ref_ptr(std::const_pointer_cast(std::move(other._ptr))); +} + +template +unique_strong_ref_ptr reinterpret_pointer_cast(unique_strong_ref_ptr const& other) +{ + return unique_strong_ref_ptr(std::reinterpret_pointer_cast(other._ptr)); +} + +template +unique_strong_ref_ptr reinterpret_pointer_cast(unique_strong_ref_ptr&& other) +{ + return unique_strong_ref_ptr(std::reinterpret_pointer_cast(std::move(other._ptr))); +} + +template +unique_strong_ref_ptr dynamic_pointer_cast(unique_strong_ref_ptr const& other) +{ + return unique_strong_ref_ptr(std::dynamic_pointer_cast(other._ptr)); +} + +template +unique_strong_ref_ptr dynamic_pointer_cast(unique_strong_ref_ptr&& other) +{ + return unique_strong_ref_ptr(std::dynamic_pointer_cast(std::move(other._ptr))); +} +} + +#endif // MANGOS_UNIQUE_TRACKABLE_PTR_H