Add custom soft pointer "object_ptr" and tests for this

This commit is contained in:
Jiga228
2025-10-17 21:11:09 +07:00
parent 938b76ac07
commit 136235749a
4 changed files with 195 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
#include "object_ptr.hpp"
std::unordered_map<void*, UType::counter::counter_owners> UType::counter::owners_map;
+164
View File
@@ -0,0 +1,164 @@
#pragma once
#include <mutex>
#include <unordered_map>
#include <list>
namespace UType
{
template<typename Ty>
class object_ptr;
class counter
{
struct counter_owners
{
std::atomic_bool is_destroyed;
std::mutex mutex;
std::list<void*> owners;
counter_owners()
: is_destroyed(false)
{
}
counter_owners(const counter_owners&) = delete;
counter_owners& operator=(const counter_owners&) = delete;
counter_owners(counter_owners&& other) noexcept
: is_destroyed(other.is_destroyed.load())
, owners(std::move(other.owners))
{
}
counter_owners& operator=(counter_owners&& other) noexcept
{
if (this != &other)
{
is_destroyed.store(other.is_destroyed.load());
owners = std::move(other.owners);
}
return *this;
}
};
static std::unordered_map<void*, counter_owners> owners_map;
public:
template<typename Ty>
static void add_owner(object_ptr<Ty>& ptr)
{
auto counter = owners_map.find(ptr.ptr_.load());
if (counter == owners_map.end())
{
counter_owners owners;
owners.owners.push_back(&ptr);
auto [it, inserted] = owners_map.try_emplace(ptr.ptr_.load(), std::move(owners));
if (!inserted)
{
ptr.ptr_.store(nullptr);
}
}
else if (!counter->second.is_destroyed)
{
std::lock_guard<std::mutex> lock(counter->second.mutex);
counter->second.owners.push_back(&ptr);
}
else
ptr.ptr_.store(nullptr);
}
template<typename Ty>
static void remove_owner(object_ptr<Ty>& ptr)
{
auto counter = owners_map.find(ptr.ptr_);
if (counter != owners_map.end())
{
counter->second.owners.remove(&ptr);
if (counter->second.is_destroyed)
{
ptr.ptr_.store(nullptr);
}
else if (counter->second.owners.empty())
{
counter->second.is_destroyed.store(true);
for (auto& owner : counter->second.owners)
static_cast<object_ptr<Ty>*>(owner)->ptr_.store(nullptr);
delete static_cast<Ty*>(counter->first);
owners_map.erase(counter);
}
}
}
template<typename Ty>
static void remove_object(Ty* ptr)
{
auto counter = owners_map.find(ptr);
if (counter != owners_map.end())
{
counter->second.is_destroyed.store(true);
for (auto& owner : counter->second.owners)
static_cast<object_ptr<Ty>*>(owner)->ptr_.store(nullptr);
delete ptr;
owners_map.erase(counter);
}
}
};
template<typename Ty>
class object_ptr
{
std::atomic<Ty*> ptr_;
public:
object_ptr(Ty* ptr = nullptr)
{
ptr_.store(ptr);
if (ptr) counter::add_owner<Ty>(*this);
}
object_ptr(const object_ptr& ptr)
{
ptr_.store(ptr.ptr_);
if (ptr.ptr_) counter::add_owner<Ty>(*this);
}
object_ptr(object_ptr&& ptr) noexcept
{
ptr_.store(ptr.ptr_);
if (ptr.ptr_) counter::add_owner<Ty>(*this);
ptr.ptr_.store(nullptr);
}
~object_ptr()
{
if (ptr_) counter::remove_owner<Ty>(*this);
}
void reset(Ty* new_ptr = nullptr)
{
if (ptr_) counter::remove_owner<Ty>(*this);
ptr_.store(new_ptr);
if (new_ptr) counter::add_owner<Ty>(*this);
}
void reset(const object_ptr& ptr)
{
if (ptr_) counter::remove_owner<Ty>(*this);
ptr_.store(ptr.ptr_);
if (ptr.ptr_) counter::add_owner<Ty>(*this);
}
void destroy()
{
if (ptr_) counter::remove_object<Ty>(ptr_);
}
Ty* get() const noexcept { return ptr_.load(); }
Ty& operator*() const noexcept { return *ptr_; }
Ty* operator->() const noexcept { return ptr_.load(); }
operator bool() const noexcept { return ptr_.load() != nullptr; }
friend class counter;
};
}