Skip to content

Instantly share code, notes, and snippets.

@Shauren
Created March 11, 2024 12:24
Show Gist options
  • Save Shauren/92a7abdd089cd7d25aa6df3fd573f2b9 to your computer and use it in GitHub Desktop.
Save Shauren/92a7abdd089cd7d25aa6df3fd573f2b9 to your computer and use it in GitHub Desktop.
UniqueTrackingPtr demo
/*
* 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"
#define TRINITYCORE_TESTING
#include "UniqueTrackingPtr.h"
struct TestObj
{
~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(new TestObj{ .Deleted = &deleted });
ptr.reset(new TestObj{});
REQUIRE(deleted == true);
}
SECTION("going out of scope deletes object")
{
REQUIRE(deleted == false);
{
Trinity::unique_trackable_ptr<TestObj> ptr(new TestObj{ .Deleted = &deleted });
}
REQUIRE(deleted == true);
}
}
struct issue_with_UniqueTrackingPtr_shared_based
{
issue_with_UniqueTrackingPtr_shared_based(Trinity::unique_weak_ptr<int> weakRef) : uniqueStrongRefPtr(weakRef.lock())
{
// captured unique_strong_ref_ptr in constructor
}
Trinity::unique_strong_ref_ptr<int> uniqueStrongRefPtr;
};
void issue_with_UniqueTrackingPtr_custom_tracking_based()
{
if (int* ptr = weakRef.lock())
{
// another thread can yeet the held object while we are here
}
}
TEST_CASE("Trinity::unique_weak_ptr", "[UniqueTrackingPtr]")
{
Trinity::unique_trackable_ptr<int> ptr(new int);
Trinity::unique_weak_ptr<int> weakRef = ptr;
if (Trinity::unique_strong_ref_ptr<int> uniqueStrongRefPtr = weakRef.lock())
{
// stuff
}
SECTION("when unique_trackable_ptr no longer holds a value then weak cannot retrieve it")
{
ptr.reset();
REQUIRE(!weakRef.lock());
}
SECTION("when unique_trackable_ptr holds a value then weak can retrieve it")
{
REQUIRE(!!weakRef.lock());
}
}
/*
* 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;
namespace Impl
{
class weak_tracking_deleter_count
{
public:
void weak_add_ref()
{
++_weak_count;
}
void weak_release()
{
if (!--_weak_count)
delete this;
}
bool valid() const
{
return _valid;
}
void invalidate()
{
_valid = false;
}
#ifndef TRINITYCORE_TESTING
private:
#endif
uint32 _weak_count = 1;
bool _valid = true;
};
template<typename T, typename Deleter = std::default_delete<T>>
class weak_tracking_deleter
{
struct inner_deleter : Deleter
{
using Deleter::Deleter;
weak_tracking_deleter_count* Count = new weak_tracking_deleter_count();
};
public:
weak_tracking_deleter() = default;
weak_tracking_deleter(weak_tracking_deleter const&) = delete;
weak_tracking_deleter(weak_tracking_deleter&& other) noexcept : _innerDeleter(std::move(other._innerDeleter))
{
other._innerDeleter.Count = nullptr;
}
weak_tracking_deleter& operator=(weak_tracking_deleter const&) = delete;
weak_tracking_deleter& operator=(weak_tracking_deleter&& other) noexcept
{
if (this != &other)
{
_innerDeleter = std::move(other._innerDeleter);
other._innerDeleter.Count = nullptr;
}
return *this;
}
~weak_tracking_deleter() = default;
void operator()(T* ptr)
{
release();
_innerDeleter(ptr);
}
void release()
{
_innerDeleter.Count->invalidate();
_innerDeleter.Count->weak_release();
_innerDeleter.Count = nullptr;
}
#ifndef TRINITYCORE_TESTING
private:
template<typename>
friend class unique_weak_ptr;
#endif
weak_tracking_deleter_count* _get_count() const
{
return _innerDeleter.Count;
}
inner_deleter _innerDeleter;
};
}
template <typename T, typename Deleter>
class unique_trackable_ptr
{
public:
using pointer = T*;
using element_type = T;
using deleter_type = Impl::weak_tracking_deleter<element_type, Deleter>;
unique_trackable_ptr() : _ptr(nullptr) { }
explicit unique_trackable_ptr(pointer ptr) : _ptr(ptr) { }
unique_trackable_ptr(unique_trackable_ptr const&) = delete;
unique_trackable_ptr(unique_trackable_ptr&& other) noexcept
: _ptr(std::exchange(other._ptr, nullptr)), _deleter(std::forward<deleter_type>(other.get_deleter()))
{ }
unique_trackable_ptr& operator=(unique_trackable_ptr const&) = delete;
unique_trackable_ptr& operator=(unique_trackable_ptr&& other) noexcept
{
unique_trackable_ptr(std::move(other)).swap(*this);
return *this;
}
~unique_trackable_ptr()
{
if (_ptr)
_deleter(_ptr);
}
unique_trackable_ptr& operator=(std::nullptr_t)
{
reset();
return *this;
}
void swap(unique_trackable_ptr& other) noexcept
{
using std::swap;
swap(_ptr, other._ptr);
swap(_deleter, _deleter);
}
deleter_type& get_deleter()
{
return _deleter;
}
deleter_type const& get_deleter() const
{
return _deleter;
}
element_type& operator*() const
{
return *_ptr;
}
pointer operator->() const
{
return _ptr;
}
pointer get() const
{
return _ptr;
}
explicit operator bool() const
{
return static_cast<bool>(_ptr);
}
pointer release()
{
_deleter.release();
return std::exchange(_ptr, nullptr);
}
void reset(pointer ptr = nullptr)
{
pointer _Old = std::exchange(_ptr, ptr);
if (_Old)
_deleter(_Old);
if (_ptr)
std::exchange(_deleter, deleter_type());
}
private:
pointer _ptr;
deleter_type _deleter;
};
template<typename T>
class unique_weak_ptr
{
public:
using pointer = T*;
unique_weak_ptr() : _ptr(nullptr), _count(nullptr) { }
unique_weak_ptr(unique_trackable_ptr<T> const& trackable) : _ptr(trackable.get()), _count(trackable.get_deleter()._get_count())
{
if (_count)
_count->weak_add_ref();
}
unique_weak_ptr(unique_weak_ptr const& other) : _ptr(other._ptr), _count(other._count)
{
if (other._count)
other._count->weak_add_ref();
}
unique_weak_ptr(unique_weak_ptr&& other) noexcept
: _ptr(std::exchange(other._ptr, nullptr)), _count(std::exchange(other._count, nullptr))
{ }
unique_weak_ptr& operator=(unique_trackable_ptr<T> const& trackable)
{
unique_weak_ptr(trackable).swap(*this);
return *this;
}
unique_weak_ptr& operator=(unique_weak_ptr const& other)
{
unique_weak_ptr(other).swap(*this);
return *this;
}
unique_weak_ptr& operator=(unique_weak_ptr&& other) noexcept
{
unique_weak_ptr(std::move(other)).swap(*this);
return *this;
}
~unique_weak_ptr()
{
if (_count)
_count->weak_release();
}
void swap(unique_weak_ptr& other) noexcept
{
std::swap(_ptr, other._ptr);
std::swap(_count, other._count);
}
pointer lock() const
{
if (_count->valid())
return _ptr;
return nullptr;
}
private:
pointer _ptr;
Impl::weak_tracking_deleter_count* _count;
};
}
#endif // TRINITYCORE_UNIQUE_TRACKING_PTR_H
/*
* 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;
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>
friend class unique_weak_ptr;
std::shared_ptr<element_type> _ptr;
};
template<typename T>
class unique_weak_ptr
{
public:
using element_type = T;
using pointer = T*;
unique_weak_ptr() = default;
unique_weak_ptr(unique_trackable_ptr<T> const& trackable) : _ptr(trackable._ptr)
{
}
unique_weak_ptr(unique_weak_ptr const& other) = default;
unique_weak_ptr(unique_weak_ptr&& other) noexcept = default;
unique_weak_ptr& operator=(unique_trackable_ptr<T> 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);
}
unique_strong_ref_ptr<element_type> lock() const
{
return unique_strong_ref_ptr<element_type>(_ptr.lock());
}
private:
std::weak_ptr<element_type> _ptr;
};
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);
}
private:
template<typename>
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;
};
}
#endif // TRINITYCORE_UNIQUE_TRACKING_PTR_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment