Add custom soft pointer "object_ptr" and tests for this
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
#include "object_ptr.hpp"
|
||||
|
||||
std::unordered_map<void*, UType::counter::counter_owners> UType::counter::owners_map;
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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})
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user