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
|
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})
|
||||||
|
|||||||
@@ -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