#pragma once #include "fwd.h" #include "utility.h" #include "intrlist.h" #include "refcount.h" #include "typetraits.h" #include "singleton.h" #include <type_traits> #include <utility> #include <util/system/compiler.h> #include <util/system/defaults.h> #include <util/system/yassert.h> template <class T, class U> using TGuardConversion = typename std::enable_if_t<std::is_convertible<U*, T*>::value>; template <class T> inline void AssertTypeComplete() { // If compiler triggers this error from destructor of your class with // smart pointer, then may be you should move the destructor definition // to the .cpp file, where type T have full definition. // // 'delete' called on pointer to incomplete type is // undefined behavior (missing destructor call/corrupted memory manager). // 'sizeof' is used to trigger compile-time error. static_assert(sizeof(T) != 0, "Type must be complete"); } template <class T> inline void CheckedDelete(T* t) { AssertTypeComplete<T>(); delete t; } template <class T> inline void CheckedArrayDelete(T* t) { AssertTypeComplete<T>(); delete[] t; } class TNoAction { public: template <class T> static inline void Destroy(T*) noexcept { } }; class TDelete { public: template <class T> static inline void Destroy(T* t) noexcept { CheckedDelete(t); } /* * special handling for nullptr - call nothing */ static inline void Destroy(std::nullptr_t) noexcept { } /* * special handling for void* - call ::operator delete() */ static void Destroy(void* t) noexcept; }; class TDeleteArray { public: template <class T> static inline void Destroy(T* t) noexcept { CheckedArrayDelete(t); } }; class TDestructor { public: template <class T> static inline void Destroy(T* t) noexcept { (void)t; t->~T(); } }; class TFree { public: template <class T> static inline void Destroy(T* t) noexcept { DoDestroy((void*)t); } private: /* * we do not want dependancy on cstdlib here... */ static void DoDestroy(void* t) noexcept; }; template <class Base, class T> class TPointerCommon { public: using TValueType = T; inline T* operator->() const noexcept { T* ptr = AsT(); Y_ASSERT(ptr); return ptr; } #ifndef __cpp_impl_three_way_comparison template <class C> inline bool operator==(const C& p) const noexcept { return (p == AsT()); } template <class C> inline bool operator!=(const C& p) const noexcept { return (p != AsT()); } #endif inline explicit operator bool() const noexcept { return nullptr != AsT(); } protected: inline T* AsT() const noexcept { return (static_cast<const Base*>(this))->Get(); } static inline T* DoRelease(T*& t) noexcept { T* ret = t; t = nullptr; return ret; } }; template <class Base, class T> class TPointerBase: public TPointerCommon<Base, T> { public: inline T& operator*() const noexcept { Y_ASSERT(this->AsT()); return *(this->AsT()); } inline T& operator[](size_t n) const noexcept { Y_ASSERT(this->AsT()); return (this->AsT())[n]; } }; /* * void*-like pointers does not have operator* */ template <class Base> class TPointerBase<Base, void>: public TPointerCommon<Base, void> { }; template <class T, class D> class TAutoPtr: public TPointerBase<TAutoPtr<T, D>, T> { public: inline TAutoPtr(T* t = nullptr) noexcept : T_(t) { } inline TAutoPtr(const TAutoPtr& t) noexcept : T_(t.Release()) { } inline ~TAutoPtr() { DoDestroy(); } inline TAutoPtr& operator=(const TAutoPtr& t) noexcept { if (this != &t) { Reset(t.Release()); } return *this; } inline T* Release() const noexcept Y_WARN_UNUSED_RESULT { return this->DoRelease(T_); } Y_REINITIALIZES_OBJECT inline void Reset(T* t) noexcept { if (T_ != t) { DoDestroy(); T_ = t; } } Y_REINITIALIZES_OBJECT inline void Reset() noexcept { Destroy(); } inline void Destroy() noexcept { Reset(nullptr); } inline void Swap(TAutoPtr& r) noexcept { DoSwap(T_, r.T_); } inline T* Get() const noexcept { return T_; } #ifdef __cpp_impl_three_way_comparison template <class Other> inline bool operator==(const Other& p) const noexcept { return (p == Get()); } #endif private: inline void DoDestroy() noexcept { if (T_) { D::Destroy(T_); } } private: mutable T* T_; }; template <class T, class D> class THolder: public TPointerBase<THolder<T, D>, T> { public: constexpr THolder() noexcept : T_(nullptr) { } constexpr THolder(std::nullptr_t) noexcept : T_(nullptr) { } explicit THolder(T* t) noexcept : T_(t) { } inline THolder(TAutoPtr<T, D> t) noexcept : T_(t.Release()) { } template <class U, class = TGuardConversion<T, U>> inline THolder(TAutoPtr<U, D> t) noexcept : T_(t.Release()) { } inline THolder(THolder&& that) noexcept : T_(that.Release()) { } template <class U, class = TGuardConversion<T, U>> inline THolder(THolder<U, D>&& that) noexcept : T_(that.Release()) { } THolder(const THolder&) = delete; THolder& operator=(const THolder&) = delete; inline ~THolder() { DoDestroy(); } inline void Destroy() noexcept { Reset(nullptr); } inline T* Release() noexcept Y_WARN_UNUSED_RESULT { return this->DoRelease(T_); } Y_REINITIALIZES_OBJECT inline void Reset(T* t) noexcept { if (T_ != t) { DoDestroy(); T_ = t; } } Y_REINITIALIZES_OBJECT inline void Reset(TAutoPtr<T, D> t) noexcept { Reset(t.Release()); } Y_REINITIALIZES_OBJECT inline void Reset() noexcept { Destroy(); } inline void Swap(THolder& r) noexcept { DoSwap(T_, r.T_); } inline T* Get() const noexcept { return T_; } inline operator TAutoPtr<T, D>() noexcept { return Release(); } THolder& operator=(std::nullptr_t) noexcept { this->Reset(nullptr); return *this; } THolder& operator=(THolder&& that) noexcept { this->Reset(that.Release()); return *this; } template <class U> THolder& operator=(THolder<U, D>&& that) noexcept { this->Reset(that.Release()); return *this; } #ifdef __cpp_impl_three_way_comparison template <class Other> inline bool operator==(const Other& p) const noexcept { return (p == Get()); } #endif private: inline void DoDestroy() noexcept { if (T_) { D::Destroy(T_); } } private: T* T_; }; template <typename T, typename... Args> [[nodiscard]] THolder<T> MakeHolder(Args&&... args) { return THolder<T>(new T(std::forward<Args>(args)...)); } /* * usage: * class T: public TRefCounted<T> * and we get methods Ref() && UnRef() with * proper destruction of last UnRef() */ template <class T, class C, class D> class TRefCounted { public: inline TRefCounted(long initval = 0) noexcept : Counter_(initval) { } inline ~TRefCounted() = default; inline void Ref(intptr_t d) noexcept { auto resultCount = Counter_.Add(d); Y_ASSERT(resultCount >= d); (void)resultCount; } inline void Ref() noexcept { auto resultCount = Counter_.Inc(); Y_ASSERT(resultCount != 0); (void)resultCount; } inline void UnRef(intptr_t d) noexcept { auto resultCount = Counter_.Sub(d); Y_ASSERT(resultCount >= 0); if (resultCount == 0) { D::Destroy(static_cast<T*>(this)); } } inline void UnRef() noexcept { UnRef(1); } inline intptr_t RefCount() const noexcept { return Counter_.Val(); } inline void DecRef() noexcept { auto resultCount = Counter_.Dec(); Y_ASSERT(resultCount >= 0); (void)resultCount; } TRefCounted(const TRefCounted&) : Counter_(0) { } void operator=(const TRefCounted&) { } private: C Counter_; }; /** * Atomically reference-counted base with a virtual destructor. * * @note Plays well with inheritance, should be used for refcounted base classes. */ struct TThrRefBase: public TRefCounted<TThrRefBase, TAtomicCounter> { virtual ~TThrRefBase(); }; /** * Atomically reference-counted base. * * Deletes refcounted object as type T. * * @warning Additional care should be taken with regard to inheritance. If used * as a base class, @p T should either declare a virtual destructor, or be * derived from @p TThrRefBase instead. Otherwise, only destructor of class @p T * would be called, potentially slicing the object and creating memory leaks. * * @note To avoid accidental inheritance when it is not originally intended, * class @p T should be marked as final. */ template <class T, class D = TDelete> using TAtomicRefCount = TRefCounted<T, TAtomicCounter, D>; /** * Non-atomically reference-counted base. * * @warning Not thread-safe. Use with great care. If in doubt, use @p ThrRefBase * or @p TAtomicRefCount instead. */ template <class T, class D = TDelete> using TSimpleRefCount = TRefCounted<T, TSimpleCounter, D>; template <class T> class TDefaultIntrusivePtrOps { public: static inline void Ref(T* t) noexcept { Y_ASSERT(t); t->Ref(); } static inline void UnRef(T* t) noexcept { Y_ASSERT(t); t->UnRef(); } static inline void DecRef(T* t) noexcept { Y_ASSERT(t); t->DecRef(); } static inline long RefCount(const T* t) noexcept { Y_ASSERT(t); return t->RefCount(); } }; template <class T, class Ops> class TIntrusivePtr: public TPointerBase<TIntrusivePtr<T, Ops>, T> { template <class U, class O> friend class TIntrusivePtr; template <class U, class O> friend class TIntrusiveConstPtr; public: struct TNoIncrement { }; inline TIntrusivePtr(T* t = nullptr) noexcept : T_(t) { Ops(); Ref(); } inline TIntrusivePtr(T* t, TNoIncrement) noexcept : T_(t) { Ops(); } inline ~TIntrusivePtr() { UnRef(); } inline TIntrusivePtr(const TIntrusivePtr& p) noexcept : T_(p.T_) { Ref(); } // NOTE: // without std::enable_if_t compiler sometimes tries to use this constructor inappropriately // e.g. // struct A {}; // struct B {}; // void Func(TIntrusivePtr<A>); // void Func(TIntrusivePtr<B>); // ... // Func(TIntrusivePtr<A>(new A)); // <--- compiler can't decide which version of Func to use template <class U, class = TGuardConversion<T, U>> inline TIntrusivePtr(const TIntrusivePtr<U>& p) noexcept : T_(p.Get()) { Ref(); } template <class U, class = TGuardConversion<T, U>> inline TIntrusivePtr(TIntrusivePtr<U>&& p) noexcept : T_(p.T_) { p.T_ = nullptr; } inline TIntrusivePtr(TIntrusivePtr&& p) noexcept : T_(nullptr) { Swap(p); } inline TIntrusivePtr& operator=(TIntrusivePtr p) noexcept { p.Swap(*this); return *this; } // Effectively replace both: // Reset(const TIntrusivePtr&) // Reset(TIntrusivePtr&&) Y_REINITIALIZES_OBJECT inline void Reset(TIntrusivePtr t) noexcept { Swap(t); } Y_REINITIALIZES_OBJECT inline void Reset() noexcept { Drop(); } inline T* Get() const noexcept { return T_; } inline void Swap(TIntrusivePtr& r) noexcept { DoSwap(T_, r.T_); } inline void Drop() noexcept { TIntrusivePtr(nullptr).Swap(*this); } inline T* Release() const noexcept Y_WARN_UNUSED_RESULT { T* res = T_; if (T_) { Ops::DecRef(T_); T_ = nullptr; } return res; } inline long RefCount() const noexcept { return T_ ? Ops::RefCount(T_) : 0; } #ifdef __cpp_impl_three_way_comparison template <class Other> inline bool operator==(const Other& p) const noexcept { return (p == Get()); } #endif private: inline void Ref() noexcept { if (T_) { Ops::Ref(T_); } } inline void UnRef() noexcept { if (T_) { Ops::UnRef(T_); } } private: mutable T* T_; }; template <class T, class Ops> struct THash<TIntrusivePtr<T, Ops>>: THash<const T*> { using THash<const T*>::operator(); inline size_t operator()(const TIntrusivePtr<T, Ops>& ptr) const { return THash<const T*>::operator()(ptr.Get()); } }; // Behaves like TIntrusivePtr but returns const T* to prevent user from accidentally modifying the referenced object. template <class T, class Ops> class TIntrusiveConstPtr: public TPointerBase<TIntrusiveConstPtr<T, Ops>, const T> { public: inline TIntrusiveConstPtr(T* t = nullptr) noexcept // we need a non-const pointer to Ref(), UnRef() and eventually delete it. : T_(t) { Ops(); Ref(); } inline ~TIntrusiveConstPtr() { UnRef(); } inline TIntrusiveConstPtr(const TIntrusiveConstPtr& p) noexcept : T_(p.T_) { Ref(); } inline TIntrusiveConstPtr(TIntrusiveConstPtr&& p) noexcept : T_(nullptr) { Swap(p); } inline TIntrusiveConstPtr(TIntrusivePtr<T> p) noexcept : T_(p.T_) { p.T_ = nullptr; } template <class U, class = TGuardConversion<T, U>> inline TIntrusiveConstPtr(const TIntrusiveConstPtr<U>& p) noexcept : T_(p.T_) { Ref(); } template <class U, class = TGuardConversion<T, U>> inline TIntrusiveConstPtr(TIntrusiveConstPtr<U>&& p) noexcept : T_(p.T_) { p.T_ = nullptr; } inline TIntrusiveConstPtr& operator=(TIntrusiveConstPtr p) noexcept { p.Swap(*this); return *this; } // Effectively replace both: // Reset(const TIntrusiveConstPtr&) // Reset(TIntrusiveConstPtr&&) Y_REINITIALIZES_OBJECT inline void Reset(TIntrusiveConstPtr t) noexcept { Swap(t); } Y_REINITIALIZES_OBJECT inline void Reset() noexcept { Drop(); } inline const T* Get() const noexcept { return T_; } inline void Swap(TIntrusiveConstPtr& r) noexcept { DoSwap(T_, r.T_); } inline void Drop() noexcept { TIntrusiveConstPtr(nullptr).Swap(*this); } inline long RefCount() const noexcept { return T_ ? Ops::RefCount(T_) : 0; } #ifdef __cpp_impl_three_way_comparison template <class Other> inline bool operator==(const Other& p) const noexcept { return (p == Get()); } #endif private: inline void Ref() noexcept { if (T_ != nullptr) { Ops::Ref(T_); } } inline void UnRef() noexcept { if (T_ != nullptr) { Ops::UnRef(T_); } } private: T* T_; template <class U, class O> friend class TIntrusiveConstPtr; }; template <class T, class Ops> struct THash<TIntrusiveConstPtr<T, Ops>>: THash<const T*> { using THash<const T*>::operator(); inline size_t operator()(const TIntrusiveConstPtr<T, Ops>& ptr) const { return THash<const T*>::operator()(ptr.Get()); } }; template <class T, class Ops> class TSimpleIntrusiveOps { using TFunc = void (*)(T*) #if __cplusplus >= 201703 noexcept #endif ; static void DoRef(T* t) noexcept { Ops::Ref(t); } static void DoUnRef(T* t) noexcept { Ops::UnRef(t); } public: inline TSimpleIntrusiveOps() noexcept { InitStaticOps(); } inline ~TSimpleIntrusiveOps() = default; static inline void Ref(T* t) noexcept { Ref_(t); } static inline void UnRef(T* t) noexcept { UnRef_(t); } private: static inline void InitStaticOps() noexcept { struct TInit { inline TInit() noexcept { Ref_ = DoRef; UnRef_ = DoUnRef; } }; Singleton<TInit>(); } private: static TFunc Ref_; static TFunc UnRef_; }; template <class T, class Ops> typename TSimpleIntrusiveOps<T, Ops>::TFunc TSimpleIntrusiveOps<T, Ops>::Ref_ = nullptr; template <class T, class Ops> typename TSimpleIntrusiveOps<T, Ops>::TFunc TSimpleIntrusiveOps<T, Ops>::UnRef_ = nullptr; template <typename T, class Ops = TDefaultIntrusivePtrOps<T>, typename... Args> [[nodiscard]] TIntrusivePtr<T, Ops> MakeIntrusive(Args&&... args) { return new T{std::forward<Args>(args)...}; } template <typename T, class Ops = TDefaultIntrusivePtrOps<T>, typename... Args> [[nodiscard]] TIntrusiveConstPtr<T, Ops> MakeIntrusiveConst(Args&&... args) { return new T{std::forward<Args>(args)...}; } template <class T, class C, class D> class TSharedPtr: public TPointerBase<TSharedPtr<T, C, D>, T> { template <class TT, class CC, class DD> friend class TSharedPtr; public: inline TSharedPtr() noexcept : T_(nullptr) , C_(nullptr) { } inline TSharedPtr(T* t) { THolder<T, D> h(t); Init(h); } inline TSharedPtr(TAutoPtr<T, D> t) { Init(t); } inline TSharedPtr(T* t, C* c) noexcept : T_(t) , C_(c) { } template <class TT, class = TGuardConversion<T, TT>> inline TSharedPtr(THolder<TT>&& t) { Init(t); } inline ~TSharedPtr() { UnRef(); } inline TSharedPtr(const TSharedPtr& t) noexcept : T_(t.T_) , C_(t.C_) { Ref(); } inline TSharedPtr(TSharedPtr&& t) noexcept : T_(nullptr) , C_(nullptr) { Swap(t); } template <class TT, class = TGuardConversion<T, TT>> inline TSharedPtr(const TSharedPtr<TT, C, D>& t) noexcept : T_(t.T_) , C_(t.C_) { Ref(); } template <class TT, class = TGuardConversion<T, TT>> inline TSharedPtr(TSharedPtr<TT, C, D>&& t) noexcept : T_(t.T_) , C_(t.C_) { t.T_ = nullptr; t.C_ = nullptr; } inline TSharedPtr& operator=(TSharedPtr t) noexcept { t.Swap(*this); return *this; } // Effectively replace both: // Reset(const TSharedPtr& t) // Reset(TSharedPtr&& t) Y_REINITIALIZES_OBJECT inline void Reset(TSharedPtr t) noexcept { Swap(t); } Y_REINITIALIZES_OBJECT inline void Reset() noexcept { Drop(); } inline void Drop() noexcept { TSharedPtr().Swap(*this); } inline T* Get() const noexcept { return T_; } inline C* ReferenceCounter() const noexcept { return C_; } inline void Swap(TSharedPtr& r) noexcept { DoSwap(T_, r.T_); DoSwap(C_, r.C_); } inline long RefCount() const noexcept { return C_ ? C_->Val() : 0; } template <class TT> [[nodiscard]] inline TSharedPtr<TT, C, D> As() & noexcept { static_assert(std::has_virtual_destructor<TT>(), "Type should have a virtual dtor"); static_assert(std::is_base_of<T, TT>(), "When downcasting from T to TT, T should be a parent of TT"); if (const auto ttPtr = dynamic_cast<TT*>(T_)) { TSharedPtr<TT, C, D> ttSharedPtr(ttPtr, C_); ttSharedPtr.Ref(); return ttSharedPtr; } else { return TSharedPtr<TT, C, D>{}; } } template <class TT> [[nodiscard]] inline TSharedPtr<TT, C, D> As() && noexcept { static_assert(std::has_virtual_destructor<TT>(), "Type should have a virtual dtor"); static_assert(std::is_base_of<T, TT>(), "When downcasting from T to TT, T should be a parent of TT"); if (const auto ttPtr = dynamic_cast<TT*>(T_)) { TSharedPtr<TT, C, D> ttSharedPtr(ttPtr, C_); T_ = nullptr; C_ = nullptr; return ttSharedPtr; } else { return TSharedPtr<TT, C, D>{}; } } #ifdef __cpp_impl_three_way_comparison template <class Other> inline bool operator==(const Other& p) const noexcept { return (p == Get()); } #endif private: template <class X> inline void Init(X& t) { C_ = !!t ? new C(1) : nullptr; T_ = t.Release(); } inline void Ref() noexcept { if (C_) { C_->Inc(); } } inline void UnRef() noexcept { if (C_ && !C_->Dec()) { DoDestroy(); } } inline void DoDestroy() noexcept { if (T_) { D::Destroy(T_); } delete C_; } private: T* T_; C* C_; }; template <class T, class C, class D> struct THash<TSharedPtr<T, C, D>>: THash<const T*> { using THash<const T*>::operator(); inline size_t operator()(const TSharedPtr<T, C, D>& ptr) const { return THash<const T*>::operator()(ptr.Get()); } }; template <class T, class D = TDelete> using TAtomicSharedPtr = TSharedPtr<T, TAtomicCounter, D>; // use with great care. if in doubt, use TAtomicSharedPtr instead template <class T, class D = TDelete> using TSimpleSharedPtr = TSharedPtr<T, TSimpleCounter, D>; template <typename T, typename C, typename... Args> [[nodiscard]] TSharedPtr<T, C> MakeShared(Args&&... args) { return new T{std::forward<Args>(args)...}; } template <typename T, typename... Args> [[nodiscard]] inline TAtomicSharedPtr<T> MakeAtomicShared(Args&&... args) { return MakeShared<T, TAtomicCounter>(std::forward<Args>(args)...); } template <typename T, typename... Args> [[nodiscard]] inline TSimpleSharedPtr<T> MakeSimpleShared(Args&&... args) { return MakeShared<T, TSimpleCounter>(std::forward<Args>(args)...); } class TCopyClone { public: template <class T> static inline T* Copy(T* t) { if (t) return t->Clone(); return nullptr; } }; class TCopyNew { public: template <class T> static inline T* Copy(T* t) { if (t) return new T(*t); return nullptr; } }; template <class T, class C, class D> class TCopyPtr: public TPointerBase<TCopyPtr<T, C, D>, T> { public: inline TCopyPtr(T* t = nullptr) noexcept : T_(t) { } inline TCopyPtr(const TCopyPtr& t) : T_(C::Copy(t.Get())) { } inline TCopyPtr(TCopyPtr&& t) noexcept : T_(nullptr) { Swap(t); } inline ~TCopyPtr() { DoDestroy(); } inline TCopyPtr& operator=(TCopyPtr t) noexcept { t.Swap(*this); return *this; } inline T* Release() noexcept Y_WARN_UNUSED_RESULT { return DoRelease(T_); } Y_REINITIALIZES_OBJECT inline void Reset(T* t) noexcept { if (T_ != t) { DoDestroy(); T_ = t; } } Y_REINITIALIZES_OBJECT inline void Reset() noexcept { Destroy(); } inline void Destroy() noexcept { Reset(nullptr); } inline void Swap(TCopyPtr& r) noexcept { DoSwap(T_, r.T_); } inline T* Get() const noexcept { return T_; } #ifdef __cpp_impl_three_way_comparison template <class Other> inline bool operator==(const Other& p) const noexcept { return (p == Get()); } #endif private: inline void DoDestroy() noexcept { if (T_) D::Destroy(T_); } private: T* T_; }; // Copy-on-write pointer template <class TPtr, class TCopy> class TCowPtr: public TPointerBase<TCowPtr<TPtr, TCopy>, const typename TPtr::TValueType> { using T = typename TPtr::TValueType; public: inline TCowPtr() = default; inline TCowPtr(const TPtr& p) : T_(p) { } inline TCowPtr(T* p) : T_(p) { } inline const T* Get() const noexcept { return Const(); } inline const T* Const() const noexcept { return T_.Get(); } inline T* Mutable() { Unshare(); return T_.Get(); } inline bool Shared() const noexcept { return T_.RefCount() > 1; } inline void Swap(TCowPtr& r) noexcept { T_.Swap(r.T_); } Y_REINITIALIZES_OBJECT inline void Reset(TCowPtr p) { p.Swap(*this); } Y_REINITIALIZES_OBJECT inline void Reset() { T_.Reset(); } #ifdef __cpp_impl_three_way_comparison template <class Other> inline bool operator==(const Other& p) const noexcept { return (p == Get()); } #endif private: inline void Unshare() { if (Shared()) { Reset(TCopy::Copy(T_.Get())); } } private: TPtr T_; }; // saves .Get() on argument passing. Intended usage: Func(TPtrArg<X> p); ... TIntrusivePtr<X> p2; Func(p2); template <class T> class TPtrArg { T* Ptr; public: TPtrArg(T* p) : Ptr(p) { } TPtrArg(const TIntrusivePtr<T>& p) : Ptr(p.Get()) { } operator T*() const { return Ptr; } T* operator->() const { return Ptr; } T* Get() const { return Ptr; } };