From 136235749a3a98d965e8a384cf109cce7c2a090c Mon Sep 17 00:00:00 2001 From: Jiga228 Date: Fri, 17 Oct 2025 21:11:09 +0700 Subject: [PATCH] Add custom soft pointer "object_ptr" and tests for this --- Core/Types/object_ptr.cpp | 3 + Core/Types/object_ptr.hpp | 164 ++++++++++++++++++++++++++++++++++++++ Tests/CMakeLists.txt | 2 + Tests/ObjectPtrTest.cpp | 26 ++++++ 4 files changed, 195 insertions(+) create mode 100644 Core/Types/object_ptr.cpp create mode 100644 Core/Types/object_ptr.hpp create mode 100644 Tests/ObjectPtrTest.cpp diff --git a/Core/Types/object_ptr.cpp b/Core/Types/object_ptr.cpp new file mode 100644 index 0000000..eb83a39 --- /dev/null +++ b/Core/Types/object_ptr.cpp @@ -0,0 +1,3 @@ +#include "object_ptr.hpp" + +std::unordered_map UType::counter::owners_map; \ No newline at end of file diff --git a/Core/Types/object_ptr.hpp b/Core/Types/object_ptr.hpp new file mode 100644 index 0000000..1e144b5 --- /dev/null +++ b/Core/Types/object_ptr.hpp @@ -0,0 +1,164 @@ + +#pragma once + +#include +#include +#include + +namespace UType +{ + template + class object_ptr; + + class counter + { + struct counter_owners + { + std::atomic_bool is_destroyed; + std::mutex mutex; + std::list 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 owners_map; + + public: + template +static void add_owner(object_ptr& 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 lock(counter->second.mutex); + counter->second.owners.push_back(&ptr); + } + else + ptr.ptr_.store(nullptr); + } + + template + static void remove_owner(object_ptr& 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*>(owner)->ptr_.store(nullptr); + delete static_cast(counter->first); + owners_map.erase(counter); + } + } + } + + template + 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*>(owner)->ptr_.store(nullptr); + delete ptr; + owners_map.erase(counter); + } + } + }; + + template + class object_ptr + { + std::atomic ptr_; + public: + object_ptr(Ty* ptr = nullptr) + { + ptr_.store(ptr); + if (ptr) counter::add_owner(*this); + } + + object_ptr(const object_ptr& ptr) + { + ptr_.store(ptr.ptr_); + if (ptr.ptr_) counter::add_owner(*this); + } + + object_ptr(object_ptr&& ptr) noexcept + { + ptr_.store(ptr.ptr_); + if (ptr.ptr_) counter::add_owner(*this); + ptr.ptr_.store(nullptr); + } + + ~object_ptr() + { + if (ptr_) counter::remove_owner(*this); + } + + void reset(Ty* new_ptr = nullptr) + { + if (ptr_) counter::remove_owner(*this); + ptr_.store(new_ptr); + if (new_ptr) counter::add_owner(*this); + } + + void reset(const object_ptr& ptr) + { + if (ptr_) counter::remove_owner(*this); + ptr_.store(ptr.ptr_); + if (ptr.ptr_) counter::add_owner(*this); + } + + void destroy() + { + if (ptr_) counter::remove_object(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; + }; +} \ No newline at end of file diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 3c605de..696181f 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -12,8 +12,10 @@ enable_testing() file(GLOB SRC "SaveMapTest.cpp" + "ObjectPtrTest.cpp" "${PROJECT_SOURCE_DIR}/Core/Game/SaveMap/SaveMap.cpp" + "${PROJECT_SOURCE_DIR}/Core/Types/object_ptr.cpp" ) add_executable(Tests ${SRC}) diff --git a/Tests/ObjectPtrTest.cpp b/Tests/ObjectPtrTest.cpp new file mode 100644 index 0000000..dafe324 --- /dev/null +++ b/Tests/ObjectPtrTest.cpp @@ -0,0 +1,26 @@ +#include +#include "Types/object_ptr.hpp" + +TEST(object_ptr_test, base_test) +{ + int* test = new int(123); + UType::object_ptr ptr(test); + EXPECT_EQ(*ptr.get(), 123); + + UType::object_ptr 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 ptr(new int(123)), ptr2(ptr); + + ptr.destroy(); + EXPECT_EQ(ptr.get(), nullptr); + EXPECT_EQ(ptr2.get(), nullptr); +} \ No newline at end of file