#pragma once #include "fwd.h" #include "utility.h" #include "intrlist.h" #include "refcount.h" #include "typetraits.h" #include "singleton.h" #include #include #include #include #include template using TGuardConversion = typename std::enable_if_t::value>; template 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 inline void CheckedDelete(T* t) { AssertTypeComplete(); delete t; } template inline void CheckedArrayDelete(T* t) { AssertTypeComplete(); delete[] t; } class TNoAction { public: template static inline void Destroy(T*) noexcept { } }; class TDelete { public: template 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 static inline void Destroy(T* t) noexcept { CheckedArrayDelete(t); } }; class TDestructor { public: template static inline void Destroy(T* t) noexcept { (void)t; t->~T(); } }; class TFree { public: template 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 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 inline bool operator==(const C& p) const noexcept { return (p == AsT()); } template 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(this))->Get(); } static inline T* DoRelease(T*& t) noexcept { T* ret = t; t = nullptr; return ret; } }; template class TPointerBase: public TPointerCommon { 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 TPointerBase: public TPointerCommon { }; template class TAutoPtr: public TPointerBase, 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 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 THolder: public TPointerBase, 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) noexcept : T_(t.Release()) { } template > inline THolder(TAutoPtr t) noexcept : T_(t.Release()) { } inline THolder(THolder&& that) noexcept : T_(that.Release()) { } template > inline THolder(THolder&& 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) 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() 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 THolder& operator=(THolder&& that) noexcept { this->Reset(that.Release()); return *this; } #ifdef __cpp_impl_three_way_comparison template 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 [[nodiscard]] THolder MakeHolder(Args&&... args) { return THolder(new T(std::forward(args)...)); } /* * usage: * class T: public TRefCounted * and we get methods Ref() && UnRef() with * proper destruction of last UnRef() */ template 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(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 { 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 using TAtomicRefCount = TRefCounted; /** * Non-atomically reference-counted base. * * @warning Not thread-safe. Use with great care. If in doubt, use @p ThrRefBase * or @p TAtomicRefCount instead. */ template using TSimpleRefCount = TRefCounted; template 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 TIntrusivePtr: public TPointerBase, T> { template friend class TIntrusivePtr; template 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); // void Func(TIntrusivePtr); // ... // Func(TIntrusivePtr(new A)); // <--- compiler can't decide which version of Func to use template > inline TIntrusivePtr(const TIntrusivePtr& p) noexcept : T_(p.Get()) { Ref(); } template > inline TIntrusivePtr(TIntrusivePtr&& 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 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 struct THash>: THash { using THash::operator(); inline size_t operator()(const TIntrusivePtr& ptr) const { return THash::operator()(ptr.Get()); } }; // Behaves like TIntrusivePtr but returns const T* to prevent user from accidentally modifying the referenced object. template class TIntrusiveConstPtr: public TPointerBase, 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 p) noexcept : T_(p.T_) { p.T_ = nullptr; } template > inline TIntrusiveConstPtr(const TIntrusiveConstPtr& p) noexcept : T_(p.T_) { Ref(); } template > inline TIntrusiveConstPtr(TIntrusiveConstPtr&& 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 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 friend class TIntrusiveConstPtr; }; template struct THash>: THash { using THash::operator(); inline size_t operator()(const TIntrusiveConstPtr& ptr) const { return THash::operator()(ptr.Get()); } }; template 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(); } private: static TFunc Ref_; static TFunc UnRef_; }; template typename TSimpleIntrusiveOps::TFunc TSimpleIntrusiveOps::Ref_ = nullptr; template typename TSimpleIntrusiveOps::TFunc TSimpleIntrusiveOps::UnRef_ = nullptr; template , typename... Args> [[nodiscard]] TIntrusivePtr MakeIntrusive(Args&&... args) { return new T{std::forward(args)...}; } template , typename... Args> [[nodiscard]] TIntrusiveConstPtr MakeIntrusiveConst(Args&&... args) { return new T{std::forward(args)...}; } template class TSharedPtr: public TPointerBase, T> { template friend class TSharedPtr; public: inline TSharedPtr() noexcept : T_(nullptr) , C_(nullptr) { } inline TSharedPtr(T* t) { THolder h(t); Init(h); } inline TSharedPtr(TAutoPtr t) { Init(t); } inline TSharedPtr(T* t, C* c) noexcept : T_(t) , C_(c) { } template > inline TSharedPtr(THolder&& 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 > inline TSharedPtr(const TSharedPtr& t) noexcept : T_(t.T_) , C_(t.C_) { Ref(); } template > inline TSharedPtr(TSharedPtr&& 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 [[nodiscard]] inline TSharedPtr As() & noexcept { static_assert(std::has_virtual_destructor(), "Type should have a virtual dtor"); static_assert(std::is_base_of(), "When downcasting from T to TT, T should be a parent of TT"); if (const auto ttPtr = dynamic_cast(T_)) { TSharedPtr ttSharedPtr(ttPtr, C_); ttSharedPtr.Ref(); return ttSharedPtr; } else { return TSharedPtr{}; } } template [[nodiscard]] inline TSharedPtr As() && noexcept { static_assert(std::has_virtual_destructor(), "Type should have a virtual dtor"); static_assert(std::is_base_of(), "When downcasting from T to TT, T should be a parent of TT"); if (const auto ttPtr = dynamic_cast(T_)) { TSharedPtr ttSharedPtr(ttPtr, C_); T_ = nullptr; C_ = nullptr; return ttSharedPtr; } else { return TSharedPtr{}; } } #ifdef __cpp_impl_three_way_comparison template inline bool operator==(const Other& p) const noexcept { return (p == Get()); } #endif private: template 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 struct THash>: THash { using THash::operator(); inline size_t operator()(const TSharedPtr& ptr) const { return THash::operator()(ptr.Get()); } }; template using TAtomicSharedPtr = TSharedPtr; // use with great care. if in doubt, use TAtomicSharedPtr instead template using TSimpleSharedPtr = TSharedPtr; template [[nodiscard]] TSharedPtr MakeShared(Args&&... args) { return new T{std::forward(args)...}; } template [[nodiscard]] inline TAtomicSharedPtr MakeAtomicShared(Args&&... args) { return MakeShared(std::forward(args)...); } template [[nodiscard]] inline TSimpleSharedPtr MakeSimpleShared(Args&&... args) { return MakeShared(std::forward(args)...); } class TCopyClone { public: template static inline T* Copy(T* t) { if (t) return t->Clone(); return nullptr; } }; class TCopyNew { public: template static inline T* Copy(T* t) { if (t) return new T(*t); return nullptr; } }; template class TCopyPtr: public TPointerBase, 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 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 TCowPtr: public TPointerBase, 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 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 p); ... TIntrusivePtr p2; Func(p2); template class TPtrArg { T* Ptr; public: TPtrArg(T* p) : Ptr(p) { } TPtrArg(const TIntrusivePtr& p) : Ptr(p.Get()) { } operator T*() const { return Ptr; } T* operator->() const { return Ptr; } T* Get() const { return Ptr; } };