diff --git a/docs/object_context.md b/docs/object_context.md index 19c44e6..608b2ab 100644 --- a/docs/object_context.md +++ b/docs/object_context.md @@ -6,10 +6,10 @@ Object context can be thought as a collection of uLib::Object instances. And the ## 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. +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 as a shared_ptr. -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. +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 all the references to it are removed. +For this reason the access to a object context for a Object via Get/Set must be done using the SmartPointer instances. ## SmartPointer access diff --git a/docs/smart_pointer.md b/docs/smart_pointer.md new file mode 100644 index 0000000..7ecfe12 --- /dev/null +++ b/docs/smart_pointer.md @@ -0,0 +1,120 @@ +# SmartPointer Documentation + +`uLib::SmartPointer` is a shared ownership smart pointer implementation designed for the `uLib` ecosystem. While it shares many similarities with `std::shared_ptr`, it includes specific features for legacy compatibility, local reference wrapping, and integrated Boost serialization. + +## Table of Contents +1. [Overview](#overview) +2. [Key Differences from std::shared_ptr](#key-differences-from-stdshared_ptr) +3. [Common Usage](#common-usage) +4. [Construction and Assignment](#construction-and-assignment) +5. [Wrapping References](#wrapping-references) +6. [Polymorphism and Casting](#polymorphism-and-casting) +7. [Serialization](#serialization) +8. [Thread Safety](#thread-safety) + +--- + +## Overview + +A `SmartPointer` manages the lifetime of an object through reference counting. When the last `SmartPointer` owning an object is destroyed or reset, the object is automatically deleted (unless a custom deleter is provided). + +The implementation uses an internal `ControlBlock` to manage the reference count and an optional deleter function. + +## Key Differences from std::shared_ptr + +| Feature | `uLib::SmartPointer` | `std::shared_ptr` | +| :--- | :--- | :--- | +| **Default Constructor** | Initializes to `nullptr`. | Initializes to `nullptr`. | +| **Implicit Conversion** | Implicitly converts from `T*` and to `T*`. | Explicit construction from `T*`, no implicit conversion to `T*`. | +| **Reference Wrapping** | Direct support for wrapping `T&` with a no-op deleter. | Requires explicit custom deleter `[](T*){}`. | +| **Serialization** | Built-in Boost.Serialization support. | Requires external serialization helpers. | + +> [!NOTE] +> The default constructor `SmartPointer()` now initializes to `nullptr`, matching standard C++ smart pointer behavior. + +## Common Usage + +```cpp +#include "Core/SmartPointer.h" + +// 1. Allocation via default constructor (Allocates a new MyObject) +uLib::SmartPointer ptr; + +// 2. Explicit null pointer +uLib::SmartPointer null_ptr(nullptr); + +// 3. From raw pointer +uLib::SmartPointer manual_ptr(new MyObject(args)); + +// 4. Accessing members +ptr->DoSomething(); +(*ptr).Value = 10; + +// 5. Checking validity +if (ptr) { + // ... +} +``` + +## Construction and Assignment + +### Allocation and Ownership +- `SmartPointer()`: Initializes to `nullptr` (Standard behavior). +- `SmartPointer(nullptr)`: Initializes to null. +- `SmartPointer(T* ptr)`: Takes ownership of the raw pointer (implicit conversion allowed). +- `SmartPointer(T& ref)`: Wraps an existing reference with a no-op deleter (implicit conversion allowed). +- `SmartPointer(T* ptr, Deleter d)`: Takes ownership and uses a custom deleter. +- `SmartPointer(const SmartPointer* other)`: Creates a copy from a *pointer* to another `SmartPointer`. + +### Move and Copy +- Supports standard copy and move semantics. Move operations transfer ownership without incrementing the reference count. + +### Pointer Access +- `get()` / `Get()`: Returns the underlying raw pointer. +- `operator T*()`: Implicit conversion to raw pointer (legacy support). + +## Wrapping References + +The `SmartPointer` can wrap an existing object (e.g., on the stack) without taking ownership: + +```cpp +MyObject stackObj; +uLib::SmartPointer spt(stackObj); +// spt will NOT delete stackObj when it goes out of scope. +``` + +## Polymorphism and Casting + +`SmartPointer` supports assignment between compatible types (base/derived). For explicit casting, use the following utilities: + +- `uLib::static_pointer_cast(ptr)` +- `uLib::dynamic_pointer_cast(ptr)` +- `uLib::const_pointer_cast(ptr)` +- `uLib::reinterpret_pointer_cast(ptr)` + +Example: +```cpp +uLib::SmartPointer derived(new Derived()); +uLib::SmartPointer base = derived; // Automatic upcast + +auto derived2 = uLib::dynamic_pointer_cast(base); // Downcast +``` + +## Serialization + +`SmartPointer` is fully integrated with `boost::serialization`. It tracks `ControlBlock` identity during serialization to ensure that multiple shared pointers to the same object are correctly reconstructed as a single shared instance. + +```cpp +#include + +void save(const uLib::SmartPointer& ptr, std::ostream& os) { + boost::archive::text_oarchive oa(os); + oa << ptr; +} +``` + +## Thread Safety + +- The reference count is managed using `std::atomic`, making the increment/decrement operations thread-safe. +- **Note**: While the reference counter itself is thread-safe, the object pointed to by the `SmartPointer` is not automatically protected. Standard thread-safety rules for the underlying type `T` apply. +- Multiple threads can read the same `SmartPointer` concurrently. Concurrent modification (assignment/reset) of the *same* `SmartPointer` instance by different threads requires external synchronization. diff --git a/src/Core/SmartPointer.h b/src/Core/SmartPointer.h index dd9f006..6ada9d6 100644 --- a/src/Core/SmartPointer.h +++ b/src/Core/SmartPointer.h @@ -90,11 +90,7 @@ public: } }; - SmartPointer() : m_counter(nullptr) { - if constexpr (std::is_default_constructible_v) { - m_counter = new ReferenceCounter(new T()); - } - } + SmartPointer() noexcept : m_counter(nullptr) {} SmartPointer(std::nullptr_t) noexcept : m_counter(nullptr) {} diff --git a/src/Core/testing/SerializeTest.cpp b/src/Core/testing/SerializeTest.cpp index c54260a..17737ac 100644 --- a/src/Core/testing/SerializeTest.cpp +++ b/src/Core/testing/SerializeTest.cpp @@ -214,7 +214,7 @@ int test_referece_serialization() { } int test_referece_smartpointer_serialization() { - SmartPointer a; + SmartPointer a(new A()); a->init_properties(); { C c, c2; c.m_a = a; c2.m_a = a; diff --git a/src/Core/testing/SmartPointerTest.cpp b/src/Core/testing/SmartPointerTest.cpp index 6305351..bbec871 100644 --- a/src/Core/testing/SmartPointerTest.cpp +++ b/src/Core/testing/SmartPointerTest.cpp @@ -74,15 +74,10 @@ int main () { TEST1(test_smpt(spt)); } + // TEST NULL POINTER // { SmartPointer spt; - TEST1(test_smpt(spt)); - } - - { - SmartPointer base_spt; - SmartPointer spt = &base_spt; - TEST1(test_smpt(spt)); + TEST1(!spt); } // TAKE REFERENCE // @@ -115,5 +110,27 @@ int main () { TEST1(spt4->Value() == 101112); } + { + SmartPointer spt = new Test::ObjectMock; + spt->Value() = 12345; + TEST1(spt->Value() == 12345); + + SmartPointer spt2 = spt; + TEST1(spt2->Value() == 12345); + TEST1(spt.use_count() == 2); + } + + { + // Using new with custom deleter + bool deleted = false; + { + SmartPointer spt(new int(10), [&](int* p) { + deleted = true; + delete p; + }); + TEST1(*spt == 10); + } + TEST1(deleted == true); + } END_TESTING; }