/*** Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: ExtendStlLikeSharedPtr.hpp Date: 2022-1-25 Author: Reece ***/ #pragma once //#include #include namespace Aurora::Memory { inline void ThrowNullException(); namespace _detail { struct IPtrGet { virtual void *Get() = 0; }; struct IPtrNoOpGet : IPtrGet { inline virtual void *Get() override { ThrowNullException(); return {}; } }; inline IPtrNoOpGet gNoop; } template struct ExSharedPtr : Base_t #if !defined(_AURORA_NULLEXPT_ENABLE_UB) && !defined(_AURORA_NULLEXPT_BRANCH) , private _detail::IPtrGet #endif { using element_type = typename Base_t::element_type; using weak_type = typename Base_t::weak_type; using base_type = Base_t; using Base_t::Base_t; ExSharedPtr() : Base_t() { #if !defined(_AURORA_NULLEXPT_ENABLE_UB) _cache(); #endif } ExSharedPtr(Base_t &&in) : Base_t(in) { #if !defined(_AURORA_NULLEXPT_ENABLE_UB) _cache(); #endif } ExSharedPtr(ExSharedPtr &&in) : Base_t(in) { #if !defined(_AURORA_NULLEXPT_ENABLE_UB) _cache(); #endif } ExSharedPtr(const ExSharedPtr &in) : Base_t(in) { #if !defined(_AURORA_NULLEXPT_ENABLE_UB) _cache(); #endif } template ExSharedPtr(const ExSharedPtr &in) : Base_t(in.BasePointerType(), static_cast(in.get())) { #if !defined(_AURORA_NULLEXPT_ENABLE_UB) _cache(); #endif } template ExSharedPtr(ExSharedPtr &&in) : Base_t(in.BasePointerType(), static_cast(in.get())) { #if !defined(_AURORA_NULLEXPT_ENABLE_UB) _cache(); #endif } template ExSharedPtr(ExSharedPtr &&in, element_type *ptr) : Base_t(AuMove(in.BasePointerType()), ptr) { #if !defined(_AURORA_NULLEXPT_ENABLE_UB) _cache(); #endif } ExSharedPtr(Base_t &&in, element_type *ptr) : Base_t(in, ptr) { #if !defined(_AURORA_NULLEXPT_ENABLE_UB) _cache(); #endif } template ExSharedPtr(const ExSharedPtr &in, element_type *ptr) : Base_t(in.BasePointerType(), ptr) { #if !defined(_AURORA_NULLEXPT_ENABLE_UB) _cache(); #endif } ExSharedPtr(const Base_t &in, element_type *ptr) : Base_t(in, ptr) { #if !defined(_AURORA_NULLEXPT_ENABLE_UB) _cache(); #endif } ExSharedPtr(const Base_t &in) : Base_t(in) { #if !defined(_AURORA_NULLEXPT_ENABLE_UB) _cache(); #endif } template ExSharedPtr(Y *in, Deleter_t del, Alloc_t alloc) : Base_t(in, del, alloc) { #if !defined(_AURORA_NULLEXPT_ENABLE_UB) _cache(); #endif } template ExSharedPtr(Y *in, Deleter_t del) : Base_t(in, del) { #if !defined(_AURORA_NULLEXPT_ENABLE_UB) _cache(); #endif } template < class Y, class Deleter > ExSharedPtr(AURORA_RUNTIME_AU_UNIQUE_PTR &&r) : Base_t(AuMove(r)) { #if !defined(_AURORA_NULLEXPT_ENABLE_UB) _cache(); #endif } template void swap(ExSharedPtr &in) { Base_t::swap(in.BasePointerType()); #if !defined(_AURORA_NULLEXPT_ENABLE_UB) _cache(); #endif } void swap(Base_t &r) { Base_t::swap(r); #if !defined(_AURORA_NULLEXPT_ENABLE_UB) _cache(); #endif } void reset() { Base_t::reset(); #if !defined(_AURORA_NULLEXPT_ENABLE_UB) #if !defined(_AURORA_NULLEXPT_BRANCH) ptr = &_detail::gNoop; #endif #endif } operator const Base_t &() const noexcept { return *this; } // required for move casts operator Base_t &() noexcept { return *this; } const Base_t &BasePointerType() const noexcept { return *this; } Base_t &BasePointerType() noexcept { return *this; } operator bool() const noexcept { return Base_t::operator bool(); } ExSharedPtr &operator =(const Base_t &in) noexcept { Base_t::operator=(in); #if !defined(_AURORA_NULLEXPT_ENABLE_UB) _cache(); #endif return *this; } template ExSharedPtr &operator =(ExSharedPtr &&in) noexcept { Base_t::operator=(AuMove(in.BasePointerType())); #if !defined(_AURORA_NULLEXPT_ENABLE_UB) _cache(); #endif return *this; } ExSharedPtr &operator =(Base_t &&in) noexcept { Base_t::operator=(in); #if !defined(_AURORA_NULLEXPT_ENABLE_UB) _cache(); #endif return *this; } ExSharedPtr &operator =(const ExSharedPtr &in) noexcept { Base_t::operator=(in); #if !defined(_AURORA_NULLEXPT_ENABLE_UB) _cache(); #endif return *this; } template ExSharedPtr &operator =(const ExSharedPtr &in) noexcept { Base_t::operator=(in.BasePointerType()); #if !defined(_AURORA_NULLEXPT_ENABLE_UB) _cache(); #endif return *this; } template TType2_t &operator*() const { return *operator->(); } template TType2_t *operator->() const { #if defined(_AURORA_NULLEXPT_ENABLE_UB) return Base_t::operator->(); #elif !defined(_AURORA_NULLEXPT_BRANCH) return reinterpret_cast(ptr->Get()); #else throwif(); return Base_t::operator->(); #endif } template TType2_t &operator*() { return *operator->(); } template TType2_t *operator->() { #if defined(_AURORA_NULLEXPT_ENABLE_UB) return Base_t::operator->(); #elif !defined(_AURORA_NULLEXPT_BRANCH) return reinterpret_cast(ptr->Get()); #else throwif(); return Base_t::operator->(); #endif } element_type *get() const { return Base_t::get(); } #define ADD_OPERATOR(op) \ template \ bool operator op(const T &rhs) noexcept \ { \ return static_cast(*this) op(rhs); \ } \ \ template < class T > \ bool operator op(std::nullptr_t rhs) noexcept \ { \ return static_cast(*this) op(rhs); \ } ADD_OPERATOR(==) ADD_OPERATOR(!=) #if defined(AU_LANG_CPP_20) template < class T > std::strong_ordering operator<=>(const T &rhs) noexcept { return Base_t::operator<=>(rhs); } #else ADD_OPERATOR(>) ADD_OPERATOR(<) ADD_OPERATOR(<=) ADD_OPERATOR(=>) #endif private: #if defined(_AURORA_NULLEXPT_ENABLE_UB) void _cache() {} #elif !defined(_AURORA_NULLEXPT_BRANCH) _detail::IPtrGet * ptr; inline virtual void *Get() override { return (void *)Base_t::operator->(); } auline void _cache() { if (Base_t::operator bool()) { ptr = this; } else { ptr = &_detail::gNoop; } } #else bool cached {}; auline void throwif() const { if (!cached) [[unlikely]] { #if defined(_AURORA_NULLEXPT_BRANCH_BUG_CHECK) if (!Base_t::operator bool()) [[likely]] #endif { AU_THROW_STRING("ExSharedPointer Null Access Violation"); } } } auline void throwif() { if (!cached) [[unlikely]] { #if defined(_AURORA_NULLEXPT_BRANCH_BUG_CHECK) cached = Base_t::operator bool(); if (!cached) [[likely]] #endif { AU_THROW_STRING("ExSharedPointer Null Access Violation"); } } } auline void _cache() { cached = Base_t::operator bool(); } #endif }; template struct ExSharedFromThis : Base_t { ExSharedPtr> SharedFromThis() { return Base_t::shared_from_this(); } }; }