refactor: modernize SmartPointer with thread-safe reference counting, move semantics, and custom deleter support, and add corresponding unit test.
This commit is contained in:
@@ -23,89 +23,239 @@
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
|
||||
|
||||
#ifndef U_CORE_SMARTPOINTER_H
|
||||
#define U_CORE_SMARTPOINTER_H
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
namespace uLib {
|
||||
|
||||
/**
|
||||
* @brief A smart pointer implementation inspired by std::shared_ptr.
|
||||
*
|
||||
* Features modernized C++11/14/17 syntax, thread-safe reference counting,
|
||||
* move semantics, and support for custom deleters.
|
||||
*
|
||||
* NOTE: Default constructor allocates a new T following legacy behavior.
|
||||
*/
|
||||
template <typename T>
|
||||
class SmartPointer {
|
||||
typedef T element_type;
|
||||
public:
|
||||
using element_type = T;
|
||||
|
||||
explicit
|
||||
SmartPointer(T* ptr = NULL) : m_counter(0) {
|
||||
if(!ptr) ptr = new T;
|
||||
/**
|
||||
* @brief Constructor from raw pointer.
|
||||
* If ptr is nullptr, a new T is allocated (legacy behavior).
|
||||
*/
|
||||
explicit SmartPointer(T* ptr = nullptr) : m_counter(nullptr) {
|
||||
if (!ptr) ptr = new T();
|
||||
if (ptr) m_counter = new ReferenceCounter(ptr);
|
||||
}
|
||||
|
||||
// TakeReference //
|
||||
SmartPointer(T &ref) : m_counter(new ReferenceCounter(&ref,0)) { }
|
||||
|
||||
SmartPointer(const SmartPointer& copy) throw () {
|
||||
acquire(copy.m_counter);
|
||||
/**
|
||||
* @brief Constructor with custom deleter.
|
||||
*/
|
||||
template <typename D>
|
||||
SmartPointer(T* ptr, D deleter) : m_counter(nullptr) {
|
||||
if (ptr) m_counter = new ReferenceCounter(ptr, deleter);
|
||||
}
|
||||
|
||||
SmartPointer(SmartPointer * copy) throw () {
|
||||
acquire(copy->m_counter);
|
||||
/**
|
||||
* @brief Non-owning constructor from reference.
|
||||
* Uses a no-op deleter to ensure the referenced object is not destroyed.
|
||||
*/
|
||||
SmartPointer(T &ref) : m_counter(new ReferenceCounter(&ref, [](T*){}, 1)) { }
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
*/
|
||||
SmartPointer(const SmartPointer& other) noexcept : m_counter(nullptr) {
|
||||
acquire(other.m_counter);
|
||||
}
|
||||
|
||||
virtual ~SmartPointer() { release(); }
|
||||
/**
|
||||
* @brief Copy constructor from a pointer to SmartPointer (Legacy support).
|
||||
*/
|
||||
SmartPointer(const SmartPointer* other) noexcept : m_counter(nullptr) {
|
||||
if (other) acquire(other->m_counter);
|
||||
}
|
||||
|
||||
SmartPointer & operator=(const SmartPointer& copy) {
|
||||
if (this != ©)
|
||||
{
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
*/
|
||||
SmartPointer(SmartPointer&& other) noexcept : m_counter(other.m_counter) {
|
||||
other.m_counter = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Virtual destructor.
|
||||
*/
|
||||
virtual ~SmartPointer() { release(); }
|
||||
|
||||
/**
|
||||
* @brief Copy assignment.
|
||||
*/
|
||||
SmartPointer& operator=(const SmartPointer& other) noexcept {
|
||||
if (this != &other) {
|
||||
release();
|
||||
acquire(copy.m_counter);
|
||||
acquire(other.m_counter);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
T & operator*() const throw () { return *m_counter->ptr; }
|
||||
T * operator->() const throw () { return m_counter->ptr; }
|
||||
|
||||
T * get() const throw () {
|
||||
return m_counter ? m_counter->ptr : 0; }
|
||||
bool unique() const throw () {
|
||||
return (m_counter ? m_counter->count == 1 : true); }
|
||||
|
||||
|
||||
private:
|
||||
|
||||
struct ReferenceCounter
|
||||
{
|
||||
ReferenceCounter(T* ptr = 0, unsigned c = 1) : ptr(ptr), count(c) { }
|
||||
T* ptr;
|
||||
unsigned count;
|
||||
} * m_counter;
|
||||
|
||||
// increment the count
|
||||
void acquire(ReferenceCounter* c) throw ()
|
||||
{
|
||||
m_counter = c;
|
||||
if (c && c->count>0) ++c->count;
|
||||
/**
|
||||
* @brief Move assignment.
|
||||
*/
|
||||
SmartPointer& operator=(SmartPointer&& other) noexcept {
|
||||
if (this != &other) {
|
||||
release();
|
||||
m_counter = other.m_counter;
|
||||
other.m_counter = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// decrement the count, delete if it is 0
|
||||
void release() {
|
||||
if (m_counter) {
|
||||
if (--m_counter->count == 0) {
|
||||
delete m_counter->ptr;
|
||||
}
|
||||
if (m_counter->count <= 0) {
|
||||
delete m_counter;
|
||||
m_counter = NULL;
|
||||
}
|
||||
/**
|
||||
* @brief Resets the smart pointer to hold a new raw pointer.
|
||||
*/
|
||||
void reset(T* ptr = nullptr) {
|
||||
release();
|
||||
if (ptr) m_counter = new ReferenceCounter(ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resets the smart pointer with a custom deleter.
|
||||
*/
|
||||
template <typename D>
|
||||
void reset(T* ptr, D deleter) {
|
||||
release();
|
||||
if (ptr) m_counter = new ReferenceCounter(ptr, deleter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Swaps contents with another SmartPointer.
|
||||
*/
|
||||
void swap(SmartPointer& other) noexcept {
|
||||
std::swap(m_counter, other.m_counter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Dereference operator.
|
||||
*/
|
||||
T& operator*() const noexcept { return *m_counter->ptr; }
|
||||
|
||||
/**
|
||||
* @brief Member access operator.
|
||||
*/
|
||||
T* operator->() const noexcept { return m_counter->ptr; }
|
||||
|
||||
/**
|
||||
* @brief Returns the raw pointer.
|
||||
*/
|
||||
T* get() const noexcept { return m_counter ? m_counter->ptr : nullptr; }
|
||||
|
||||
/**
|
||||
* @brief Returns the number of SmartPointers sharing ownership.
|
||||
*/
|
||||
uint32_t use_count() const noexcept {
|
||||
return m_counter ? m_counter->count.load(std::memory_order_relaxed) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if this is the only SmartPointer owning the resource.
|
||||
*/
|
||||
bool unique() const noexcept { return use_count() == 1; }
|
||||
|
||||
/**
|
||||
* @brief Boolean conversion operator.
|
||||
*/
|
||||
explicit operator bool() const noexcept { return get() != nullptr; }
|
||||
|
||||
private:
|
||||
struct ReferenceCounter {
|
||||
T* ptr;
|
||||
std::atomic<uint32_t> count;
|
||||
std::function<void(T*)> deleter;
|
||||
|
||||
ReferenceCounter(T* p, uint32_t initial_count = 1)
|
||||
: ptr(p), count(initial_count), deleter([](T* ptr_to_del) { delete ptr_to_del; }) {}
|
||||
|
||||
template <typename D>
|
||||
ReferenceCounter(T* p, D d, uint32_t initial_count = 1)
|
||||
: ptr(p), count(initial_count), deleter(d) {}
|
||||
};
|
||||
|
||||
ReferenceCounter* m_counter;
|
||||
|
||||
void acquire(ReferenceCounter* c) noexcept {
|
||||
m_counter = c;
|
||||
if (c) {
|
||||
c->count.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
void release() noexcept {
|
||||
if (m_counter) {
|
||||
if (m_counter->count.fetch_sub(1, std::memory_order_acq_rel) == 1) {
|
||||
if (m_counter->ptr) {
|
||||
m_counter->deleter(m_counter->ptr);
|
||||
}
|
||||
delete m_counter;
|
||||
}
|
||||
m_counter = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Global swap for SmartPointer.
|
||||
*/
|
||||
template <typename T>
|
||||
void swap(SmartPointer<T>& a, SmartPointer<T>& b) noexcept {
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
#endif // SMARTPOINTER_H
|
||||
/**
|
||||
* @brief Equality comparison.
|
||||
*/
|
||||
template <typename T, typename U>
|
||||
bool operator==(const SmartPointer<T>& a, const SmartPointer<U>& b) noexcept {
|
||||
return a.get() == b.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inequality comparison.
|
||||
*/
|
||||
template <typename T, typename U>
|
||||
bool operator!=(const SmartPointer<T>& a, const SmartPointer<U>& b) noexcept {
|
||||
return a.get() != b.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Comparison with nullptr.
|
||||
*/
|
||||
template <typename T>
|
||||
bool operator==(const SmartPointer<T>& a, std::nullptr_t) noexcept {
|
||||
return a.get() == nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator==(std::nullptr_t, const SmartPointer<T>& a) noexcept {
|
||||
return a.get() == nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator!=(const SmartPointer<T>& a, std::nullptr_t) noexcept {
|
||||
return a.get() != nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator!=(std::nullptr_t, const SmartPointer<T>& a) noexcept {
|
||||
return a.get() != nullptr;
|
||||
}
|
||||
|
||||
} // namespace uLib
|
||||
|
||||
#endif // U_CORE_SMARTPOINTER_H
|
||||
|
||||
@@ -139,7 +139,6 @@ typedef id_t Id_t;
|
||||
typedef void *Pointer_t;
|
||||
typedef bool Bool_t; // Boolean (0=false, 1=true) (bool)
|
||||
|
||||
|
||||
//--- bit manipulation ---------------------------------------------------------
|
||||
#ifndef BIT
|
||||
#define BIT(n) (1ULL << (n))
|
||||
@@ -182,7 +181,7 @@ typedef bool Bool_t; // Boolean (0=false, 1=true) (bool)
|
||||
\
|
||||
public: \
|
||||
typedef type_info::BaseClass BaseClass; \
|
||||
virtual const char *type_name() const override { return type_info::name; } \
|
||||
virtual const char *type_name() const override { return type_info::name; } \
|
||||
/**/
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# TESTS
|
||||
set( TESTS
|
||||
SmartVectorTest
|
||||
SmartPointerTest
|
||||
VectorTest
|
||||
ObjectFlagsTest
|
||||
ObjectParametersTest
|
||||
|
||||
Reference in New Issue
Block a user