diff --git a/docs/object_context.md b/docs/object_context.md index c691a74..19c44e6 100644 --- a/docs/object_context.md +++ b/docs/object_context.md @@ -12,6 +12,14 @@ The ObjectContext is responsible to keep track of all the objects that are added For this reason the access to a object context for a Object via Get/Set is done using the SmartPointer instances. +## 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. + + ## Geant Physical Volumes The Geant library add a further layer of complexity. The physical volumes are created from a what is called LogicalVolume (which holds information about the shape, material and daughter volumes) and represent the actual instances of the volumes in the detector. So in this sense they represent what could be the Prop3D in the uLib Vtk library. The PhysicalVolume is created from the LogicalVolume and is the one that is actually placed in the scene, with its own relative TRS: position and rotation (rotation here is a rotation matrix comprising the scaling). diff --git a/src/Core/SmartPointer.h b/src/Core/SmartPointer.h index 74e214f..dd9f006 100644 --- a/src/Core/SmartPointer.h +++ b/src/Core/SmartPointer.h @@ -34,30 +34,27 @@ #include #include - namespace uLib { /** - * @brief Internal control block for shared ownership across polymorphic - * SmartPointers. + * @brief Internal control block for shared ownership across polymorphic SmartPointers. */ struct ControlBlock { - std::atomic count; - std::function deleter; + std::atomic count; + std::function deleter; - explicit ControlBlock(uint32_t initial_count = 1) : count(initial_count) {} + explicit ControlBlock(uint32_t initial_count = 1) : count(initial_count) {} private: - friend class boost::serialization::access; - template - 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); - } + friend class boost::serialization::access; + template + 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); + } }; /** @@ -65,207 +62,196 @@ private: */ template class SmartPointer { public: - using element_type = T; + using element_type = T; - /** - * @brief Nested reference counter structure. - * Preserved as a nested template for Boost serialization compatibility. - */ - struct ReferenceCounter { - T *ptr; - ControlBlock *cb; + /** + * @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; }; + ReferenceCounter() : ptr(nullptr), cb(nullptr) {} + explicit ReferenceCounter(T* p) : ptr(p), cb(new ControlBlock(1)) { + cb->deleter = [p]() { delete p; }; + } + template + ReferenceCounter(T* p, D d) : ptr(p), cb(new ControlBlock(1)) { + cb->deleter = [p, d]() { d(p); }; + } + + private: + friend class boost::serialization::access; + template + 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) { + m_counter = new ReferenceCounter(new T()); + } } + + SmartPointer(std::nullptr_t) noexcept : m_counter(nullptr) {} + + /** + * @brief Constructor from raw pointer (Implicit conversion allowed for legacy compatibility). + */ + SmartPointer(T* ptr) : m_counter(nullptr) { + if (ptr) m_counter = new ReferenceCounter(ptr); + } + template - ReferenceCounter(T *p, D d) : ptr(p), cb(new ControlBlock(1)) { - cb->deleter = [p, d]() { d(p); }; + SmartPointer(T* ptr, D deleter) : m_counter(nullptr) { + if (ptr) m_counter = new ReferenceCounter(ptr, deleter); } - private: - friend class boost::serialization::access; + SmartPointer(T &ref) : m_counter(new ReferenceCounter(&ref, [](T*){})) { } + + SmartPointer(const SmartPointer& other) noexcept : m_counter(nullptr) { + acquire(other.m_counter); + } + + SmartPointer(const SmartPointer* other) noexcept : m_counter(nullptr) { + if (other) acquire(other->m_counter); + } + + template >> + SmartPointer(const SmartPointer& other) noexcept : m_counter(nullptr) { + if (other.m_counter) { + m_counter = new ReferenceCounter(); + m_counter->ptr = static_cast(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 + SmartPointer(const SmartPointer& 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; + } + + ~SmartPointer() { release(); } + + SmartPointer& operator=(const SmartPointer& other) noexcept { + if (this != &other) { + release(); + acquire(other.m_counter); + } + return *this; + } + + SmartPointer& operator=(T* ptr) noexcept { + reset(ptr); + return *this; + } + + SmartPointer& operator=(SmartPointer&& other) noexcept { + if (this != &other) { + release(); + m_counter = other.m_counter; + other.m_counter = nullptr; + } + return *this; + } + + void reset(T* ptr = nullptr) { + release(); + if (ptr) m_counter = new ReferenceCounter(ptr); + } + + void swap(SmartPointer& other) noexcept { + std::swap(m_counter, other.m_counter); + } + + T& operator*() const noexcept { return *(m_counter->ptr); } + T* operator->() const noexcept { return m_counter->ptr; } + T* get() const noexcept { return m_counter ? m_counter->ptr : nullptr; } + T* Get() const noexcept { return get(); } + operator T*() const noexcept { return get(); } + + uint32_t use_count() const noexcept { + return (m_counter && m_counter->cb) ? m_counter->cb->count.load(std::memory_order_relaxed) : 0; + } + + bool unique() const noexcept { return use_count() == 1; } + explicit operator bool() const noexcept { return get() != nullptr; } + + BOOST_SERIALIZATION_SPLIT_MEMBER() + template - void serialize(Archive &ar, const unsigned int /*version*/) { - ar &boost::serialization::make_nvp("ptr", ptr); - ar &boost::serialization::make_nvp("cb", cb); + void save(Archive& ar, const unsigned int /*version*/) const { + ar & boost::serialization::make_nvp("counter", m_counter); } - }; - SmartPointer() : m_counter(nullptr) { - if constexpr (std::is_default_constructible_v) { - m_counter = new ReferenceCounter(new T()); + template + void load(Archive& ar, const unsigned int /*version*/) { + release(); + ar & boost::serialization::make_nvp("counter", m_counter); + if (m_counter && m_counter->cb) { + m_counter->cb->count.fetch_add(1, std::memory_order_relaxed); + } } - } - - SmartPointer(std::nullptr_t) noexcept : m_counter(nullptr) {} - - /** - * @brief Constructor from raw pointer (Implicit conversion allowed for legacy - * compatibility). - */ - SmartPointer(T *ptr) : m_counter(nullptr) { - if (ptr) - m_counter = new ReferenceCounter(ptr); - } - - template SmartPointer(T *ptr, D deleter) : m_counter(nullptr) { - if (ptr) - m_counter = new ReferenceCounter(ptr, deleter); - } - - SmartPointer(T &ref) : m_counter(new ReferenceCounter(&ref, [](T *) {})) {} - - SmartPointer(const SmartPointer &other) noexcept : m_counter(nullptr) { - acquire(other.m_counter); - } - - SmartPointer(const SmartPointer *other) noexcept : m_counter(nullptr) { - if (other) - acquire(other->m_counter); - } - - template >> - SmartPointer(const SmartPointer &other) noexcept : m_counter(nullptr) { - if (other.m_counter) { - m_counter = new ReferenceCounter(); - m_counter->ptr = static_cast(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 - SmartPointer(const SmartPointer &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; - } - - ~SmartPointer() { release(); } - - SmartPointer &operator=(const SmartPointer &other) noexcept { - if (this != &other) { - release(); - acquire(other.m_counter); - } - return *this; - } - - SmartPointer &operator=(T *ptr) noexcept { - reset(ptr); - return *this; - } - - SmartPointer &operator=(SmartPointer &&other) noexcept { - if (this != &other) { - release(); - m_counter = other.m_counter; - other.m_counter = nullptr; - } - return *this; - } - - void reset(T *ptr = nullptr) { - release(); - if (ptr) - m_counter = new ReferenceCounter(ptr); - } - - void swap(SmartPointer &other) noexcept { - std::swap(m_counter, other.m_counter); - } - - T &operator*() const noexcept { return *(m_counter->ptr); } - T *operator->() const noexcept { return m_counter->ptr; } - T *get() const noexcept { return m_counter ? m_counter->ptr : nullptr; } - T *Get() const noexcept { return get(); } - operator T *() const noexcept { return get(); } - - uint32_t use_count() const noexcept { - return (m_counter && m_counter->cb) - ? m_counter->cb->count.load(std::memory_order_relaxed) - : 0; - } - - bool unique() const noexcept { return use_count() == 1; } - explicit operator bool() const noexcept { return get() != nullptr; } - - BOOST_SERIALIZATION_SPLIT_MEMBER() - - template - void save(Archive &ar, const unsigned int /*version*/) const { - ar &boost::serialization::make_nvp("counter", m_counter); - } - - template - void load(Archive &ar, const unsigned int /*version*/) { - release(); - ar &boost::serialization::make_nvp("counter", m_counter); - if (m_counter && m_counter->cb) { - m_counter->cb->count.fetch_add(1, std::memory_order_relaxed); - } - } private: - template friend class SmartPointer; - friend class boost::serialization::access; + template friend class SmartPointer; + friend class boost::serialization::access; - ReferenceCounter *m_counter; + ReferenceCounter* m_counter; - void acquire(ReferenceCounter *c) noexcept { - if (c) { - 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 acquire(ReferenceCounter* c) noexcept { + if (c) { + 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->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; + void release() noexcept { + if (m_counter) { + 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; + } } - } }; -template -SmartPointer static_pointer_cast(const SmartPointer &r) noexcept { - return SmartPointer(r, static_cast(r.get())); +template +SmartPointer static_pointer_cast(const SmartPointer& r) noexcept { + return SmartPointer(r, static_cast(r.get())); } -template -SmartPointer dynamic_pointer_cast(const SmartPointer &r) noexcept { - if (auto p = dynamic_cast(r.get())) - return SmartPointer(r, p); - return SmartPointer(nullptr); + +template +SmartPointer dynamic_pointer_cast(const SmartPointer& r) noexcept { + if (auto p = dynamic_cast(r.get())) return SmartPointer(r, p); + return SmartPointer(nullptr); } -template -SmartPointer const_pointer_cast(const SmartPointer &r) noexcept { - return SmartPointer(r, const_cast(r.get())); + +template +SmartPointer const_pointer_cast(const SmartPointer& r) noexcept { + return SmartPointer(r, const_cast(r.get())); } -template -SmartPointer reinterpret_pointer_cast(const SmartPointer &r) noexcept { - return SmartPointer(r, reinterpret_cast(r.get())); + +template +SmartPointer reinterpret_pointer_cast(const SmartPointer& r) noexcept { + return SmartPointer(r, reinterpret_cast(r.get())); } } // namespace uLib