11 Commits

2 changed files with 190 additions and 226 deletions

View File

@@ -2,7 +2,15 @@
In uLib the context is meant to hold a set of objects and their hierarchy. In addition ObjectFactory is used to create objects from a predefined registry. In uLib the context is meant to hold a set of objects and their hierarchy. In addition ObjectFactory is used to create objects from a predefined registry.
Object context can be thouught as a collection of uLib::Object instances. And there exists nested collection of objects if a context is added to another context. A nested context is a Group of elements that appears like a single object in the parent context and a hierarchy of objects inside the tree structure. Object context can be thought as a collection of uLib::Object instances. And there exists nested collection of objects if a context is added to another context. A nested context is a Group of elements that appears like a single object in the parent context and a hierarchy of objects inside the tree structure.
## SmartPointer access
SmartPointer is a class that is used to hold a reference to another object. It is a template class that can be used to hold a reference to any object that is derived from uLib::Object. It is a smart pointer because it will automatically delete the object when it is no longer needed. It is also a smart pointer because it will automatically update the object when it is no longer needed.
The ObjectContext is responsible to keep track of all the objects that are added to it and to provide a way to access them, but also it holds the SmartPointer instances that point to the objects that are added to it. In this way Objects added to a Context are disposed only when the context is destroyed.
For this reason the access to a object context for a Object via Get/Set is done using the SmartPointer instances.
## SmartPointer access ## SmartPointer access

View File

@@ -29,16 +29,15 @@
#include <atomic> #include <atomic>
#include <functional> #include <functional>
#include <type_traits> #include <type_traits>
#include <utility>
#include <boost/serialization/access.hpp> #include <boost/serialization/access.hpp>
#include <boost/serialization/nvp.hpp> #include <boost/serialization/nvp.hpp>
#include <boost/serialization/split_member.hpp>
namespace uLib { namespace uLib {
/** /**
* @brief Internal control block for shared ownership across polymorphic * @brief Internal control block for shared ownership across polymorphic SmartPointers.
* SmartPointers.
*/ */
struct ControlBlock { struct ControlBlock {
std::atomic<uint32_t> count; std::atomic<uint32_t> count;
@@ -50,12 +49,11 @@ private:
friend class boost::serialization::access; friend class boost::serialization::access;
template <class Archive> template <class Archive>
void serialize(Archive& ar, const unsigned int /*version*/) { void serialize(Archive& ar, const unsigned int /*version*/) {
// ControlBlock identity is tracked by Boost via the cb pointer in // ControlBlock identity is tracked by Boost via the cb pointer in ReferenceCounter.
// ReferenceCounter. We only save the count value. // We only save the count value.
uint32_t c = count.load(); uint32_t c = count.load();
ar & boost::serialization::make_nvp("count", c); ar & boost::serialization::make_nvp("count", c);
if constexpr (Archive::is_loading::value) if constexpr (Archive::is_loading::value) count.store(c);
count.store(c);
} }
}; };
@@ -101,17 +99,15 @@ public:
SmartPointer(std::nullptr_t) noexcept : m_counter(nullptr) {} SmartPointer(std::nullptr_t) noexcept : m_counter(nullptr) {}
/** /**
* @brief Constructor from raw pointer (Implicit conversion allowed for legacy * @brief Constructor from raw pointer (Implicit conversion allowed for legacy compatibility).
* compatibility).
*/ */
SmartPointer(T* ptr) : m_counter(nullptr) { SmartPointer(T* ptr) : m_counter(nullptr) {
if (ptr) if (ptr) m_counter = new ReferenceCounter(ptr);
m_counter = new ReferenceCounter(ptr);
} }
template <typename D> SmartPointer(T *ptr, D deleter) : m_counter(nullptr) { template <typename D>
if (ptr) SmartPointer(T* ptr, D deleter) : m_counter(nullptr) {
m_counter = new ReferenceCounter(ptr, deleter); if (ptr) m_counter = new ReferenceCounter(ptr, deleter);
} }
SmartPointer(T &ref) : m_counter(new ReferenceCounter(&ref, [](T*){})) { } SmartPointer(T &ref) : m_counter(new ReferenceCounter(&ref, [](T*){})) { }
@@ -121,31 +117,26 @@ public:
} }
SmartPointer(const SmartPointer* other) noexcept : m_counter(nullptr) { SmartPointer(const SmartPointer* other) noexcept : m_counter(nullptr) {
if (other) if (other) acquire(other->m_counter);
acquire(other->m_counter);
} }
template <typename U, template <typename U, typename = std::enable_if_t<std::is_convertible_v<U*, T*>>>
typename = std::enable_if_t<std::is_convertible_v<U *, T *>>>
SmartPointer(const SmartPointer<U>& other) noexcept : m_counter(nullptr) { SmartPointer(const SmartPointer<U>& other) noexcept : m_counter(nullptr) {
if (other.m_counter) { if (other.m_counter) {
m_counter = new ReferenceCounter(); m_counter = new ReferenceCounter();
m_counter->ptr = static_cast<T*>(other.m_counter->ptr); m_counter->ptr = static_cast<T*>(other.m_counter->ptr);
m_counter->cb = other.m_counter->cb; m_counter->cb = other.m_counter->cb;
if (m_counter->cb) if (m_counter->cb) m_counter->cb->count.fetch_add(1, std::memory_order_relaxed);
m_counter->cb->count.fetch_add(1, std::memory_order_relaxed);
} }
} }
template <typename U> template <typename U>
SmartPointer(const SmartPointer<U> &other, T *ptr) noexcept SmartPointer(const SmartPointer<U>& other, T* ptr) noexcept : m_counter(nullptr) {
: m_counter(nullptr) {
if (other.m_counter) { if (other.m_counter) {
m_counter = new ReferenceCounter(); m_counter = new ReferenceCounter();
m_counter->ptr = ptr; m_counter->ptr = ptr;
m_counter->cb = other.m_counter->cb; m_counter->cb = other.m_counter->cb;
if (m_counter->cb) if (m_counter->cb) m_counter->cb->count.fetch_add(1, std::memory_order_relaxed);
m_counter->cb->count.fetch_add(1, std::memory_order_relaxed);
} }
} }
@@ -179,8 +170,7 @@ public:
void reset(T* ptr = nullptr) { void reset(T* ptr = nullptr) {
release(); release();
if (ptr) if (ptr) m_counter = new ReferenceCounter(ptr);
m_counter = new ReferenceCounter(ptr);
} }
void swap(SmartPointer& other) noexcept { void swap(SmartPointer& other) noexcept {
@@ -194,9 +184,7 @@ public:
operator T*() const noexcept { return get(); } operator T*() const noexcept { return get(); }
uint32_t use_count() const noexcept { uint32_t use_count() const noexcept {
return (m_counter && m_counter->cb) return (m_counter && m_counter->cb) ? m_counter->cb->count.load(std::memory_order_relaxed) : 0;
? m_counter->cb->count.load(std::memory_order_relaxed)
: 0;
} }
bool unique() const noexcept { return use_count() == 1; } bool unique() const noexcept { return use_count() == 1; }
@@ -219,58 +207,24 @@ public:
} }
private: private:
template <typename U> friend class SmartPointer;
friend class boost::serialization::access; friend class boost::serialization::access;
template <class Archive>
void serialize(Archive &ar, const unsigned int /*version*/) {
if (Archive::is_loading::value) {
release();
}
ar &boost::serialization::make_nvp("counter", m_counter);
if (Archive::is_loading::value && m_counter) {
m_counter->count.fetch_add(1, std::memory_order_relaxed);
}
}
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; ReferenceCounter* m_counter;
void acquire(ReferenceCounter* c) noexcept { void acquire(ReferenceCounter* c) noexcept {
m_counter = c;
if (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 { void release() noexcept {
if (m_counter) { if (m_counter) {
if (m_counter->cb && if (m_counter->cb && m_counter->cb->count.fetch_sub(1, std::memory_order_acq_rel) == 1) {
m_counter->cb->count.fetch_sub(1, std::memory_order_acq_rel) == 1) { if (m_counter->cb->deleter) m_counter->cb->deleter();
if (m_counter->cb->deleter)
m_counter->cb->deleter();
delete m_counter->cb; delete m_counter->cb;
} }
delete m_counter; delete m_counter;
@@ -283,16 +237,18 @@ template <typename T, typename U>
SmartPointer<T> static_pointer_cast(const SmartPointer<U>& r) noexcept { SmartPointer<T> static_pointer_cast(const SmartPointer<U>& r) noexcept {
return SmartPointer<T>(r, static_cast<T*>(r.get())); return SmartPointer<T>(r, static_cast<T*>(r.get()));
} }
template <typename T, typename U> template <typename T, typename U>
SmartPointer<T> dynamic_pointer_cast(const SmartPointer<U>& r) noexcept { SmartPointer<T> dynamic_pointer_cast(const SmartPointer<U>& r) noexcept {
if (auto p = dynamic_cast<T *>(r.get())) if (auto p = dynamic_cast<T*>(r.get())) return SmartPointer<T>(r, p);
return SmartPointer<T>(r, p);
return SmartPointer<T>(nullptr); return SmartPointer<T>(nullptr);
} }
template <typename T, typename U> template <typename T, typename U>
SmartPointer<T> const_pointer_cast(const SmartPointer<U>& r) noexcept { SmartPointer<T> const_pointer_cast(const SmartPointer<U>& r) noexcept {
return SmartPointer<T>(r, const_cast<T*>(r.get())); return SmartPointer<T>(r, const_cast<T*>(r.get()));
} }
template <typename T, typename U> template <typename T, typename U>
SmartPointer<T> reinterpret_pointer_cast(const SmartPointer<U>& r) noexcept { SmartPointer<T> reinterpret_pointer_cast(const SmartPointer<U>& r) noexcept {
return SmartPointer<T>(r, reinterpret_cast<T*>(r.get())); return SmartPointer<T>(r, reinterpret_cast<T*>(r.get()));