#pragma once namespace Scaleform { template class Ptr { public: using element_type = T; // 1 constexpr Ptr() noexcept = default; // 2 constexpr Ptr(std::nullptr_t) noexcept {} // 3 template < class Y, std::enable_if_t< std::is_convertible_v< Y*, element_type*>, int> = 0> explicit Ptr(Y* a_rhs) : _ptr(a_rhs) { TryAttach(); } // 9a Ptr(const Ptr& a_rhs) : _ptr(a_rhs._ptr) { TryAttach(); } // 9b template < class Y, std::enable_if_t< std::is_convertible_v< Y*, element_type*>, int> = 0> Ptr(const Ptr& a_rhs) : _ptr(a_rhs._ptr) { TryAttach(); } // 10a Ptr(Ptr&& a_rhs) noexcept : _ptr(a_rhs._ptr) { a_rhs._ptr = nullptr; } // 10b template < class Y, std::enable_if_t< std::is_convertible_v< Y*, element_type*>, int> = 0> Ptr(Ptr&& a_rhs) noexcept : _ptr(a_rhs._ptr) { a_rhs._ptr = nullptr; } ~Ptr() { TryDetach(); } // 1a Ptr& operator=(const Ptr& a_rhs) { if (this != std::addressof(a_rhs)) { TryDetach(); _ptr = a_rhs._ptr; TryAttach(); } return *this; } // 1b template < class Y, std::enable_if_t< std::is_convertible_v< Y*, element_type*>, int> = 0> Ptr& operator=(const Ptr& a_rhs) { TryDetach(); _ptr = a_rhs._ptr; TryAttach(); return *this; } // 2a Ptr& operator=(Ptr&& a_rhs) { if (this != std::addressof(a_rhs)) { TryDetach(); _ptr = a_rhs._ptr; a_rhs._ptr = nullptr; } return *this; } // 2b template < class Y, std::enable_if_t< std::is_convertible_v< Y*, element_type*>, int> = 0> Ptr& operator=(Ptr&& a_rhs) { TryDetach(); _ptr = a_rhs._ptr; a_rhs._ptr = nullptr; return *this; } void reset() { TryDetach(); } template < class Y, std::enable_if_t< std::is_convertible_v< Y*, element_type*>, int> = 0> void reset(Y* a_ptr) { if (_ptr != a_ptr) { TryDetach(); _ptr = a_ptr; TryAttach(); } } [[nodiscard]] constexpr element_type* get() const noexcept { return _ptr; } [[nodiscard]] explicit constexpr operator bool() const noexcept { return static_cast(_ptr); } [[nodiscard]] constexpr element_type& operator*() const noexcept { assert(static_cast(*this)); return *_ptr; } [[nodiscard]] constexpr element_type* operator->() const noexcept { assert(static_cast(*this)); return _ptr; } protected: template friend class Ptr; void TryAttach() { if (_ptr) { _ptr->AddRef(); } } void TryDetach() { if (_ptr) { _ptr->Release(); _ptr = nullptr; } } // members element_type* _ptr{ nullptr }; // 0 }; static_assert(sizeof(Ptr) == 0x8); template [[nodiscard]] Ptr make_shared(Args&&... a_args) { auto ptr = Ptr{ new T(std::forward(a_args)...) }; if (ptr) { ptr->Release(); // ensure lifetime ends with smart pointer } return ptr; } template [[nodiscard]] constexpr bool operator==(const Ptr& a_lhs, const Ptr& a_rhs) { return a_lhs.get() == a_rhs.get(); } template [[nodiscard]] constexpr bool operator!=(const Ptr& a_lhs, const Ptr& a_rhs) { return !(a_lhs == a_rhs); } template [[nodiscard]] constexpr bool operator==(const Ptr& a_lhs, std::nullptr_t) noexcept { return !a_lhs; } template [[nodiscard]] constexpr bool operator==(std::nullptr_t, const Ptr& a_rhs) noexcept { return !a_rhs; } template [[nodiscard]] constexpr bool operator!=(const Ptr& a_lhs, std::nullptr_t) noexcept { return static_cast(a_lhs); } template [[nodiscard]] constexpr bool operator!=(std::nullptr_t, const Ptr& a_rhs) noexcept { return static_cast(a_rhs); } template Ptr(T*) -> Ptr; }