#pragma once #include "ref_counted.h" #include #include #include #include namespace NYT { //////////////////////////////////////////////////////////////////////////////// template class TIntrusivePtr { public: using TUnderlying = 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 notoriously hard to make this constructor explicit * given the current amount of code written. */ 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()) { static_assert( std::derived_from, "Cast allowed only for types derived from TRefCountedBase"); 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()) { static_assert( std::derived_from, "Cast allowed only for types derived from TRefCountedBase"); 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*"); static_assert( std::derived_from, "Cast allowed only for types derived from TRefCountedBase"); 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*"); static_assert( std::derived_from, "Cast allowed only for types derived from TRefCountedBase"); 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 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; }; //////////////////////////////////////////////////////////////////////////////// //! 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 bool operator<(const TIntrusivePtr& lhs, const TIntrusivePtr& rhs) { return lhs.Get() < rhs.Get(); } template bool operator>(const TIntrusivePtr& lhs, const TIntrusivePtr& rhs) { return lhs.Get() > rhs.Get(); } template bool operator==(const TIntrusivePtr& lhs, const TIntrusivePtr& rhs) { static_assert( std::is_convertible_v, "U* must be convertible to T*"); return lhs.Get() == rhs.Get(); } template bool operator!=(const TIntrusivePtr& lhs, const TIntrusivePtr& rhs) { static_assert( std::is_convertible_v, "U* must be convertible to T*"); return lhs.Get() != rhs.Get(); } template bool operator==(const TIntrusivePtr& lhs, U* rhs) { return lhs.Get() == rhs; } template bool operator!=(const TIntrusivePtr& lhs, U* rhs) { static_assert( std::is_convertible_v, "U* must be convertible to T*"); return lhs.Get() != rhs; } template bool operator==(T* lhs, const TIntrusivePtr& rhs) { return lhs == rhs.Get(); } template bool operator!=(T* lhs, const TIntrusivePtr& rhs) { static_assert( std::is_convertible_v, "U* must be convertible to T*"); return lhs != rhs.Get(); } template bool operator==(std::nullptr_t, const TIntrusivePtr& rhs) { return nullptr == 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(); } template bool operator!=(const TIntrusivePtr& lhs, std::nullptr_t) { return nullptr != lhs.Get(); } //////////////////////////////////////////////////////////////////////////////// } //namespace NYT //! A hasher for TIntrusivePtr. template struct THash> { Y_FORCE_INLINE size_t operator () (const NYT::TIntrusivePtr& ptr) const { return THash()(ptr.Get()); } };