|
|
|
|
@@ -3,7 +3,7 @@
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
Copyright (c) 2014, Universita' degli Studi di Padova, INFN sez. di Padova
|
|
|
|
|
All rights reserved
|
|
|
|
|
All Padua preserved
|
|
|
|
|
|
|
|
|
|
Authors: Andrea Rigoni Garola < andrea.rigoni@pd.infn.it >
|
|
|
|
|
|
|
|
|
|
@@ -29,25 +29,36 @@
|
|
|
|
|
#include <atomic>
|
|
|
|
|
#include <functional>
|
|
|
|
|
#include <type_traits>
|
|
|
|
|
#include <utility>
|
|
|
|
|
#include <boost/serialization/access.hpp>
|
|
|
|
|
#include <boost/serialization/nvp.hpp>
|
|
|
|
|
#include <boost/serialization/split_member.hpp>
|
|
|
|
|
#include <boost/serialization/vector.hpp>
|
|
|
|
|
#include <boost/serialization/string.hpp>
|
|
|
|
|
|
|
|
|
|
#include <boost/serialization/access.hpp>
|
|
|
|
|
#include <boost/serialization/nvp.hpp>
|
|
|
|
|
#include <boost/serialization/split_member.hpp>
|
|
|
|
|
|
|
|
|
|
namespace uLib {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Internal control block for shared ownership across polymorphic SmartPointers.
|
|
|
|
|
*/
|
|
|
|
|
struct ControlBlock {
|
|
|
|
|
std::atomic<uint32_t> count;
|
|
|
|
|
std::function<void()> deleter;
|
|
|
|
|
|
|
|
|
|
explicit ControlBlock(uint32_t initial_count = 1) : count(initial_count) {}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
friend class boost::serialization::access;
|
|
|
|
|
template <class Archive>
|
|
|
|
|
void serialize(Archive& ar, const unsigned int /*version*/) {
|
|
|
|
|
// ControlBlock identity is tracked by Boost via the cb pointer in ReferenceCounter.
|
|
|
|
|
// We only save the count value.
|
|
|
|
|
uint32_t c = count.load();
|
|
|
|
|
ar & boost::serialization::make_nvp("count", c);
|
|
|
|
|
if constexpr (Archive::is_loading::value) count.store(c);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @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 {
|
|
|
|
|
@@ -55,70 +66,87 @@ public:
|
|
|
|
|
using element_type = T;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Default constructor.
|
|
|
|
|
* Allocates a new T following legacy behavior.
|
|
|
|
|
* @brief Nested reference counter structure.
|
|
|
|
|
* Preserved as a nested template for Boost serialization compatibility.
|
|
|
|
|
*/
|
|
|
|
|
struct ReferenceCounter {
|
|
|
|
|
T* ptr;
|
|
|
|
|
ControlBlock* cb;
|
|
|
|
|
|
|
|
|
|
ReferenceCounter() : ptr(nullptr), cb(nullptr) {}
|
|
|
|
|
explicit ReferenceCounter(T* p) : ptr(p), cb(new ControlBlock(1)) {
|
|
|
|
|
cb->deleter = [p]() { delete p; };
|
|
|
|
|
}
|
|
|
|
|
template <typename D>
|
|
|
|
|
ReferenceCounter(T* p, D d) : ptr(p), cb(new ControlBlock(1)) {
|
|
|
|
|
cb->deleter = [p, d]() { d(p); };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
friend class boost::serialization::access;
|
|
|
|
|
template <class Archive>
|
|
|
|
|
void serialize(Archive& ar, const unsigned int /*version*/) {
|
|
|
|
|
ar & boost::serialization::make_nvp("ptr", ptr);
|
|
|
|
|
ar & boost::serialization::make_nvp("cb", cb);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
SmartPointer() : m_counter(nullptr) {
|
|
|
|
|
if constexpr (std::is_default_constructible_v<T>) {
|
|
|
|
|
m_counter = new ReferenceCounter(new T());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Constructor from nullptr.
|
|
|
|
|
*/
|
|
|
|
|
SmartPointer(std::nullptr_t) noexcept : m_counter(nullptr) {}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Constructor from raw pointer.
|
|
|
|
|
* @brief Constructor from raw pointer (Implicit conversion allowed for legacy compatibility).
|
|
|
|
|
*/
|
|
|
|
|
explicit SmartPointer(T* ptr) : m_counter(nullptr) {
|
|
|
|
|
SmartPointer(T* ptr) : m_counter(nullptr) {
|
|
|
|
|
if (ptr) m_counter = new ReferenceCounter(ptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Constructor with custom deleter.
|
|
|
|
|
*/
|
|
|
|
|
template <typename D>
|
|
|
|
|
SmartPointer(T* ptr, D deleter) : m_counter(nullptr) {
|
|
|
|
|
if (ptr) m_counter = new ReferenceCounter(ptr, deleter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @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)) { }
|
|
|
|
|
SmartPointer(T &ref) : m_counter(new ReferenceCounter(&ref, [](T*){})) { }
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Copy constructor.
|
|
|
|
|
*/
|
|
|
|
|
SmartPointer(const SmartPointer& other) noexcept : m_counter(nullptr) {
|
|
|
|
|
acquire(other.m_counter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Copy constructor from a pointer to SmartPointer (Legacy support).
|
|
|
|
|
*/
|
|
|
|
|
SmartPointer(const SmartPointer* other) noexcept : m_counter(nullptr) {
|
|
|
|
|
if (other) acquire(other->m_counter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Move constructor.
|
|
|
|
|
*/
|
|
|
|
|
template <typename U, typename = std::enable_if_t<std::is_convertible_v<U*, T*>>>
|
|
|
|
|
SmartPointer(const SmartPointer<U>& other) noexcept : m_counter(nullptr) {
|
|
|
|
|
if (other.m_counter) {
|
|
|
|
|
m_counter = new ReferenceCounter();
|
|
|
|
|
m_counter->ptr = static_cast<T*>(other.m_counter->ptr);
|
|
|
|
|
m_counter->cb = other.m_counter->cb;
|
|
|
|
|
if (m_counter->cb) m_counter->cb->count.fetch_add(1, std::memory_order_relaxed);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename U>
|
|
|
|
|
SmartPointer(const SmartPointer<U>& other, T* ptr) noexcept : m_counter(nullptr) {
|
|
|
|
|
if (other.m_counter) {
|
|
|
|
|
m_counter = new ReferenceCounter();
|
|
|
|
|
m_counter->ptr = ptr;
|
|
|
|
|
m_counter->cb = other.m_counter->cb;
|
|
|
|
|
if (m_counter->cb) m_counter->cb->count.fetch_add(1, std::memory_order_relaxed);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SmartPointer(SmartPointer&& other) noexcept : m_counter(other.m_counter) {
|
|
|
|
|
other.m_counter = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Virtual destructor.
|
|
|
|
|
*/
|
|
|
|
|
virtual ~SmartPointer() { release(); }
|
|
|
|
|
~SmartPointer() { release(); }
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Copy assignment.
|
|
|
|
|
*/
|
|
|
|
|
SmartPointer& operator=(const SmartPointer& other) noexcept {
|
|
|
|
|
if (this != &other) {
|
|
|
|
|
release();
|
|
|
|
|
@@ -132,9 +160,6 @@ public:
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Move assignment.
|
|
|
|
|
*/
|
|
|
|
|
SmartPointer& operator=(SmartPointer&& other) noexcept {
|
|
|
|
|
if (this != &other) {
|
|
|
|
|
release();
|
|
|
|
|
@@ -144,66 +169,26 @@ public:
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @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); }
|
|
|
|
|
T* operator->() const noexcept { return m_counter->ptr; }
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Returns the raw pointer.
|
|
|
|
|
*/
|
|
|
|
|
T* get() const noexcept { return m_counter ? m_counter->ptr : nullptr; }
|
|
|
|
|
T* Get() const noexcept { return get(); }
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Implicit conversion to raw pointer (legacy compatibility).
|
|
|
|
|
*/
|
|
|
|
|
operator T*() const noexcept { return get(); }
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @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;
|
|
|
|
|
return (m_counter && m_counter->cb) ? m_counter->cb->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; }
|
|
|
|
|
|
|
|
|
|
BOOST_SERIALIZATION_SPLIT_MEMBER()
|
|
|
|
|
@@ -217,105 +202,45 @@ public:
|
|
|
|
|
void load(Archive& ar, const unsigned int /*version*/) {
|
|
|
|
|
release();
|
|
|
|
|
ar & boost::serialization::make_nvp("counter", m_counter);
|
|
|
|
|
if (m_counter) {
|
|
|
|
|
m_counter->count.fetch_add(1, std::memory_order_relaxed);
|
|
|
|
|
if (m_counter && m_counter->cb) {
|
|
|
|
|
m_counter->cb->count.fetch_add(1, std::memory_order_relaxed);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
template <typename U> friend class SmartPointer;
|
|
|
|
|
friend class boost::serialization::access;
|
|
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
: ptr(nullptr), count(0), deleter([](T* p) { delete p; }) {}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
friend class boost::serialization::access;
|
|
|
|
|
template <class Archive>
|
|
|
|
|
void serialize(Archive& ar, const unsigned int /*version*/) {
|
|
|
|
|
ar & boost::serialization::make_nvp("ptr", ptr);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ReferenceCounter* m_counter;
|
|
|
|
|
|
|
|
|
|
void acquire(ReferenceCounter* c) noexcept {
|
|
|
|
|
m_counter = c;
|
|
|
|
|
if (c) {
|
|
|
|
|
c->count.fetch_add(1, std::memory_order_relaxed);
|
|
|
|
|
m_counter = new ReferenceCounter();
|
|
|
|
|
m_counter->ptr = c->ptr;
|
|
|
|
|
m_counter->cb = c->cb;
|
|
|
|
|
if (m_counter->cb) m_counter->cb->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);
|
|
|
|
|
if (m_counter->cb && m_counter->cb->count.fetch_sub(1, std::memory_order_acq_rel) == 1) {
|
|
|
|
|
if (m_counter->cb->deleter) m_counter->cb->deleter();
|
|
|
|
|
delete m_counter->cb;
|
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @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;
|
|
|
|
|
template <typename T, typename U> SmartPointer<T> static_pointer_cast(const SmartPointer<U>& r) noexcept { return SmartPointer<T>(r, static_cast<T*>(r.get())); }
|
|
|
|
|
template <typename T, typename U> SmartPointer<T> dynamic_pointer_cast(const SmartPointer<U>& r) noexcept {
|
|
|
|
|
if (auto p = dynamic_cast<T*>(r.get())) return SmartPointer<T>(r, p);
|
|
|
|
|
return SmartPointer<T>(nullptr);
|
|
|
|
|
}
|
|
|
|
|
template <typename T, typename U> SmartPointer<T> const_pointer_cast(const SmartPointer<U>& r) noexcept { return SmartPointer<T>(r, const_cast<T*>(r.get())); }
|
|
|
|
|
template <typename T, typename U> SmartPointer<T> reinterpret_pointer_cast(const SmartPointer<U>& r) noexcept { return SmartPointer<T>(r, reinterpret_cast<T*>(r.get())); }
|
|
|
|
|
|
|
|
|
|
} // namespace uLib
|
|
|
|
|
|
|
|
|
|
|