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;
};
}
+2
View File
@@ -12,8 +12,10 @@ enable_testing()
file(GLOB SRC file(GLOB SRC
"SaveMapTest.cpp" "SaveMapTest.cpp"
"ObjectPtrTest.cpp"
"${PROJECT_SOURCE_DIR}/Core/Game/SaveMap/SaveMap.cpp" "${PROJECT_SOURCE_DIR}/Core/Game/SaveMap/SaveMap.cpp"
"${PROJECT_SOURCE_DIR}/Core/Types/object_ptr.cpp"
) )
add_executable(Tests ${SRC}) add_executable(Tests ${SRC})
+26
View File
@@ -0,0 +1,26 @@
#include <gtest/gtest.h>
#include "Types/object_ptr.hpp"
TEST(object_ptr_test, base_test)
{
int* test = new int(123);
UType::object_ptr<int> ptr(test);
EXPECT_EQ(*ptr.get(), 123);
UType::object_ptr<int> ptr2(ptr);
EXPECT_EQ(*ptr2.get(), 123);
EXPECT_EQ(ptr2.get(), ptr.get());
ptr.destroy();
EXPECT_EQ(ptr.get(), nullptr);
EXPECT_EQ(ptr2.get(), nullptr);
}
TEST(object_ptr_test, check_destry)
{
UType::object_ptr<int> ptr(new int(123)), ptr2(ptr);
ptr.destroy();
EXPECT_EQ(ptr.get(), nullptr);
EXPECT_EQ(ptr2.get(), nullptr);
}