#pragma once #include "ref_counted.h" #include #include #include #include namespace NYT { //////////////////////////////////////////////////////////////////////////////// template class TIntrusivePtr { public: using TUnderlying = T; //! For compatibility with std:: smart pointers. using element_type = T; constexpr TIntrusivePtr() noexcept { } constexpr TIntrusivePtr(std::nullptr_t) noexcept { } //! Constructor from an unqualified reference. /*! * Note that this constructor could be racy due to unsynchronized operations * on the object and on the counter. * * Note that it is notoriously hard to make this constructor explicit * given the current amount of code written. */ constexpr TIntrusivePtr(T* obj, bool addReference = true) noexcept : T_(obj) { if (T_ && addReference) { Ref(T_); } } //! Copy constructor. TIntrusivePtr(const TIntrusivePtr& other) noexcept : T_(other.Get()) { if (T_) { Ref(T_); } } //! Copy constructor with an upcast. template >> TIntrusivePtr(const TIntrusivePtr& other) noexcept : T_(other.Get()) { ValidateCastFrom(); if (T_) { Ref(T_); } } //! Move constructor. TIntrusivePtr(TIntrusivePtr&& other) noexcept : T_(other.Get()) { other.T_ = nullptr; } //! Move constructor with an upcast. template >> TIntrusivePtr(TIntrusivePtr&& other) noexcept : T_(other.Get()) { ValidateCastFrom(); other.T_ = nullptr; } //! Destructor. ~TIntrusivePtr() { if (T_) { Unref(T_); } } //! Copy assignment operator. TIntrusivePtr& operator=(const TIntrusivePtr& other) noexcept { TIntrusivePtr(other).Swap(*this); return *this; } //! Copy assignment operator with an upcast. template TIntrusivePtr& operator=(const TIntrusivePtr& other) noexcept { static_assert( std::is_convertible_v, "U* must be convertible to T*"); ValidateCastFrom(); TIntrusivePtr(other).Swap(*this); return *this; } //! Move assignment operator. TIntrusivePtr& operator=(TIntrusivePtr&& other) noexcept { TIntrusivePtr(std::move(other)).Swap(*this); return *this; } //! Move assignment operator with an upcast. template TIntrusivePtr& operator=(TIntrusivePtr&& other) noexcept { static_assert( std::is_convertible_v, "U* must be convertible to T*"); ValidateCastFrom(); TIntrusivePtr(std::move(other)).Swap(*this); return *this; } //! Drop the pointer. void Reset() // noexcept { TIntrusivePtr().Swap(*this); } //! Replace the pointer with a specified one. void Reset(T* p) // noexcept { TIntrusivePtr(p).Swap(*this); } //! Returns the pointer. T* Get() const noexcept { return T_; } //! Returns the pointer, for compatibility with std:: smart pointers. T* get() const noexcept { return T_; } //! Returns the pointer and releases the ownership. T* Release() noexcept { auto* p = T_; T_ = nullptr; return p; } T& operator*() const noexcept { YT_ASSERT(T_); return *T_; } T* operator->() const noexcept { YT_ASSERT(T_); return T_; } explicit operator bool() const noexcept { return T_ != nullptr; } //! Swap the pointer with the other one. void Swap(TIntrusivePtr& r) noexcept { DoSwap(T_, r.T_); } private: template friend class TIntrusivePtr; T* T_ = nullptr; template static constexpr void ValidateCastFrom() noexcept { if constexpr (!std::is_same_v) { static_assert( std::derived_from, "Cast allowed only for types derived from TRefCountedBase"); } } }; //////////////////////////////////////////////////////////////////////////////// //! Creates a strong pointer wrapper for a given raw pointer. //! Compared to |TIntrusivePtr::ctor|, type inference enables omitting |T|. template TIntrusivePtr MakeStrong(T* p) { return TIntrusivePtr(p); } //! Tries to obtain an intrusive pointer for an object that may had //! already lost all of its references and, thus, is about to be deleted. /*! * You may call this method at any time provided that you have a valid * raw pointer to an object. The call either returns an intrusive pointer * for the object (thus ensuring that the object won't be destroyed until * you're holding this pointer) or NULL indicating that the last reference * had already been lost and the object is on its way to heavens. * All these steps happen atomically. * * Under all circumstances it is caller's responsibility the make sure that * the object is not destroyed during the call to #DangerousGetPtr. * Typically this is achieved by keeping a (lock-protected) collection of * raw pointers, taking a lock in object's destructor, and unregistering * its raw pointer from the collection there. */ template Y_FORCE_INLINE TIntrusivePtr DangerousGetPtr(T* object) { return object->TryRef() ? TIntrusivePtr(object, false) : TIntrusivePtr(); } //////////////////////////////////////////////////////////////////////////////// template TIntrusivePtr StaticPointerCast(const TIntrusivePtr& ptr) { return {static_cast(ptr.Get())}; } template TIntrusivePtr StaticPointerCast(TIntrusivePtr&& ptr) { return {static_cast(ptr.Release()), false}; } template TIntrusivePtr ConstPointerCast(const TIntrusivePtr& ptr) { return {const_cast(ptr.Get())}; } template TIntrusivePtr ConstPointerCast(TIntrusivePtr&& ptr) { return {const_cast(ptr.Release()), false}; } template TIntrusivePtr DynamicPointerCast(const TIntrusivePtr& ptr) { return {dynamic_cast(ptr.Get())}; } //////////////////////////////////////////////////////////////////////////////// template std::strong_ordering operator<=>(const TIntrusivePtr& lhs, const TIntrusivePtr& rhs) { return lhs.Get() <=> rhs.Get(); } template std::strong_ordering operator<=>(const TIntrusivePtr& lhs, T* rhs) { return lhs.Get() <=> rhs; } template std::strong_ordering operator<=>(T* lhs, const TIntrusivePtr& rhs) { return lhs <=> rhs.Get(); } template requires std::equality_comparable_with bool operator==(const TIntrusivePtr& lhs, const TIntrusivePtr& rhs) { return lhs.Get() == rhs.Get(); } template requires std::equality_comparable_with bool operator==(const TIntrusivePtr& lhs, U* rhs) { return lhs.Get() == rhs; } template requires std::equality_comparable_with bool operator==(T* lhs, const TIntrusivePtr& rhs) { return lhs == rhs.Get(); } template bool operator==(std::nullptr_t, const TIntrusivePtr& rhs) { return nullptr == rhs.Get(); } template bool operator==(const TIntrusivePtr& lhs, std::nullptr_t) { return nullptr == lhs.Get(); } //////////////////////////////////////////////////////////////////////////////// //! Abseil hash support for TIntrusivePtr. template THash AbslHashValue(THash hash, const TIntrusivePtr& ptr) { return THash::combine(std::move(hash), ptr.Get()); } //////////////////////////////////////////////////////////////////////////////// } // namespace NYT //! A hasher for TIntrusivePtr. template struct THash> { using is_transparent = void; Y_FORCE_INLINE size_t operator()(const NYT::TIntrusivePtr& ptr) const { return THash()(ptr.Get()); } Y_FORCE_INLINE size_t operator()(T* ptr) const { return THash()(ptr); } }; template struct TEqualTo> { using is_transparent = void; template requires std::equality_comparable_with, const U&> bool operator()(const NYT::TIntrusivePtr& lhs, const U& rhs) const { return lhs == rhs; } }; template struct TLess> { using is_transparent = void; template requires std::three_way_comparable_with&, const U&> bool operator()(const NYT::TIntrusivePtr& lhs, const U& rhs) const { return lhs < rhs; } template requires std::three_way_comparable_with bool operator()(T* lhs, const U& rhs) const { return lhs < rhs; } };