// allow-include-after-namespace #pragma once // FastLED smart pointer. // // * Make your subclasses inherit from fl::Referent. // * `class Foo: public fl::Referent {};` // * Use a macro to declare your smart pointer. // * For regular, non-template classes: // * `FASTLED_SMART_PTR(Foo)` -> `FooPtr` is now available. // * For templates use `FASTLED_SMART_PTR_NO_FWD(Foo)` // * `template class Foo {}; using FooInt = Foo;` // * `FASTLED_SAMRT_PTR_NO_FWD(FooInt)` // * `FooIntPtr` is now available. // * Instantiate from heap // * `FooPtr foo = fl::make_intrusive(a, b, ...args);` // * Use `foo->method()` to call methods. // * Instantiate from stack object and disable tracking // * `Foo foo; FooPtr fooPtr = FooPtr::NoTracking(foo);` /// #include "fl/namespace.h" #include "fl/scoped_ptr.h" #include "fl/type_traits.h" #include "fl/referent.h" #include "fl/bit_cast.h" #include "fl/int.h" #include "fl/deprecated.h" // Declares a smart pointer. FASTLED_SMART_PTR(Foo) will declare a class FooPtr // which will be a typedef of shared_ptr. After this fl::make_intrusive(...args) can be // used to create a new instance of shared_ptr. #define FASTLED_SMART_PTR(type) \ class type; \ using type##Ptr = fl::shared_ptr; #define FASTLED_SMART_PTR_STRUCT(type) \ struct type; \ using type##Ptr = fl::shared_ptr; #define FASTLED_SMART_PTR_NO_FWD(type) using type##Ptr = fl::shared_ptr; // If you have an interface class that you want to create a smart pointer for, // then you need to use this to bind it to a constructor. #define FASTLED_SMART_PTR_CONSTRUCTOR(type, constructor) \ template <> class PtrTraits { \ public: \ template static shared_ptr New(Args... args) { \ fl::shared_ptr ptr = constructor(args...); \ return ptr; \ } \ }; namespace fl { class Referent; // Inherit this if you want your object to be able to go into a // Ptr, or WeakPtr. template class Ptr; // Reference counted smart pointer base class. template class WeakPtr; // Weak reference smart pointer base class. template Ptr NewPtr(Args... args); template Ptr NewPtrNoTracking(Args... args); template class PtrTraits { public: using element_type = T; using ptr_type = Ptr; template static Ptr New(Args... args); static Ptr New(); }; // Ptr is a reference-counted smart pointer that manages the lifetime of an // object. // // It will work with any class implementing ref(), unref() and destroy(). // // Please note that this Ptr class is "sticky" to it's referent, that is, no // automatic conversion from raw pointers to Ptr or vice versa is allowed and // must be done explicitly, see the Ptr::TakeOwnership() and Ptr::NoTracking() // methods. // // To create a Ptr to a concrete object, it's best to use FASTLED_SMART_PTR(Foo) // and then use fl::make_intrusive(...) to create a new instance of Ptr. // // To create a Ptr of an interface bound to a subclass (common for driver code // or when hiding implementation) use the Ptr::TakeOwnership(new // Subclass()) method. // // For objects created statically, use Ptr::NoTracking(referent) to // create a Ptr, as this will disable reference tracking but still allow it to // be used as a Ptr. // // Example: // FASTLED_SMART_PTR(Foo); // class Foo: public fl::Referent {}; // FooPtr foo = fl::make_intrusive(); // // Example 2: (Manual binding to constructor) // class FooSubclass: public Foo {}; // Ptr bar = Ptr::TakeOwnership(new FooSubclass()); // // Example 3: (Provide your own constructor so that fl::make_intrusive() works to // create a FooSubclass) // class FooSubclass: public Foo { // Foo is an interface, FooSubclass is an // implementation. // public: // static FooPtr New(int a, int b); // }; // FASTLED_SMART_PTR_CONSTRUCTOR(Foo, FooSubclass::New); // FooPtr foo = fl::make_intrusive(1, 2); // this will now work. // NOTE: This is legacy - new code should use fl::shared_ptr instead template class Ptr : public PtrTraits { public: friend class PtrTraits; template static Ptr New(Args... args); // Used for low level allocations, typically for pointer to an // implementation where it needs to convert to a Ptr of a base class. // NOTE: Legacy method - use fl::shared_ptr constructor or fl::make_shared() instead static Ptr TakeOwnership(T *ptr) { return Ptr(ptr, true); } // Used for low level allocations, typically to handle memory that is // statically allocated where the destructor should not be called when // the refcount reaches 0. // NOTE: Legacy method - use fl::make_shared_no_tracking() instead static Ptr NoTracking(T &referent) { return Ptr(&referent, false); } static Ptr Null() { return Ptr(); } // Allow upcasting of Refs. template > // NOTE: Legacy constructor - use fl::shared_ptr instead of fl::Ptr Ptr(const Ptr &refptr); // NOTE: Legacy constructor - use fl::shared_ptr instead of fl::Ptr Ptr() : referent_(nullptr) {} // Forbidden to convert a raw pointer to a Referent into a Ptr, because // it's possible that the raw pointer comes from the stack or static memory. Ptr(T *referent) = delete; Ptr &operator=(T *referent) = delete; // NOTE: Legacy constructor - use fl::shared_ptr instead of fl::Ptr Ptr(const Ptr &other); // NOTE: Legacy constructor - use fl::shared_ptr instead of fl::Ptr Ptr(Ptr &&other) noexcept; ~Ptr(); // NOTE: Legacy assignment - use fl::shared_ptr instead of fl::Ptr Ptr &operator=(const Ptr &other); // NOTE: Legacy assignment - use fl::shared_ptr instead of fl::Ptr Ptr &operator=(Ptr &&other) noexcept; // Either returns the weakptr if it exists, or an empty weakptr. WeakPtr weakRefNoCreate() const; WeakPtr weakPtr() const { return WeakPtr(*this); } bool operator==(const T *other) const { return referent_ == other; } bool operator!=(const T *other) const { return referent_ != other; } bool operator==(const Ptr &other) const { return referent_ == other.referent_; } bool operator!=(const Ptr &other) const { return referent_ != other.referent_; } bool operator<(const Ptr &other) const { return referent_ < other.referent_; } T *get() const { return referent_; } T *operator->() const { return referent_; } T &operator*() const { return *referent_; } explicit operator bool() const noexcept { return referent_ != nullptr; } void reset(); void reset(Ptr &refptr); // Releases the pointer from reference counting from this Ptr. T *release(); void swap(Ptr &other) noexcept; bool isOwned() const { return referent_ && referent_->ref_count() > 0; } private: Ptr(T *referent, bool from_heap); T *referent_; }; // NOTE: This is legacy - new code should use fl::weak_ptr instead template class WeakPtr { public: // NOTE: Legacy constructor - use fl::weak_ptr instead of fl::WeakPtr WeakPtr() : mWeakPtr(nullptr) {} // NOTE: Legacy constructor - use fl::weak_ptr instead of fl::WeakPtr WeakPtr(const Ptr &ptr); template // NOTE: Legacy constructor - use fl::weak_ptr instead of fl::WeakPtr WeakPtr(const Ptr &ptr); // NOTE: Legacy constructor - use fl::weak_ptr instead of fl::WeakPtr WeakPtr(const WeakPtr &other); template // NOTE: Legacy constructor - use fl::weak_ptr instead of fl::WeakPtr WeakPtr(const WeakPtr &other); // NOTE: Legacy constructor - use fl::weak_ptr instead of fl::WeakPtr WeakPtr(WeakPtr &&other) noexcept; ~WeakPtr(); operator bool() const; bool operator!() const; bool operator==(const WeakPtr &other) const; bool operator!=(const WeakPtr &other) const; bool operator==(const T *other) const; bool operator==(T *other) const; bool operator==(const Ptr &other) const; bool operator!=(const T *other) const; WeakPtr &operator=(const WeakPtr &other); Ptr lock() const; bool expired() const; void reset(); fl::uptr ptr_value() const { fl::uptr val = fl::bit_cast(mWeakPtr); return val; } WeakReferent* mWeakPtr; }; template // NOTE: Legacy function - use fl::make_shared(...) instead of fl::NewPtr(...) Ptr NewPtr(Args... args); template // NOTE: Legacy function - use fl::make_shared_no_tracking() instead of fl::NewPtrNoTracking() Ptr NewPtrNoTracking(T &obj); } // namespace fl // Template implementations must always be available for instantiation // This achieves the goal of separating declarations from implementations // while ensuring templates work correctly in all compilation modes #include "fl/ptr_impl.h"