#pragma once #if !defined(MIJIN_MEMORY_DYNAMIC_POINTER_HPP_INCLUDED) #define MIJIN_MEMORY_DYNAMIC_POINTER_HPP_INCLUDED 1 #include #include #include #include "../internal/common.hpp" #include "../memory/memutil.hpp" #include "../util/concepts.hpp" #include "../util/flag.hpp" namespace mijin { MIJIN_DEFINE_FLAG(Owning); template TDeleter = AllocatorDeleter>> class DynamicPointer { public: using pointer = T*; using element_type = T; using deleter_t = TDeleter; private: std::uintptr_t mData = 0; [[no_unique_address]] TDeleter mDeleter; public: constexpr DynamicPointer(std::nullptr_t = nullptr) MIJIN_NOEXCEPT {} DynamicPointer(const DynamicPointer&) = delete; constexpr DynamicPointer(pointer ptr, Owning owning, TDeleter deleter = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v) : mData(std::bit_cast(ptr) | (owning ? 1 : 0)), mDeleter(std::move(deleter)) { MIJIN_ASSERT((std::bit_cast(ptr) & 1) == 0, "Invalid address, DynamicPointer requires addresses to be divisible by two."); } template requires (std::is_assignable_v && std::is_constructible_v) constexpr DynamicPointer(DynamicPointer&& other) MIJIN_NOEXCEPT_IF((std::is_nothrow_convertible_v)) : DynamicPointer(other.get(), other.isOwning() ? Owning::YES : Owning::NO, TDeleter(std::move(other.mDeleter))) { other.mData = 0; } constexpr ~DynamicPointer() noexcept { reset(); } DynamicPointer& operator=(const DynamicPointer&) = delete; DynamicPointer& operator=(std::nullptr_t) MIJIN_NOEXCEPT { reset(); return *this; } template requires(std::is_assignable_v) DynamicPointer& operator=(DynamicPointer&& other) MIJIN_NOEXCEPT_IF((std::is_nothrow_assignable_v)) { if (this != &other) { reset(); mData = std::exchange(other.mData, 0); mDeleter = std::move(other.mDeleter); } return *this; } // template requires (std::is_base_of_v) // constexpr operator DynamicPointer() && // MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v>) // { // const Owning owning = isOwning() ? Owning::YES : Owning::NO; // T* const ptr = release(); // // return DynamicPointer(static_cast(ptr), owning, TOtherDeleter(std::move(mDeleter))); // } template requires(std::equality_comparable_with) auto operator<=>(const DynamicPointer& other) MIJIN_NOEXCEPT { return mData <=> other.mData; } constexpr bool operator==(std::nullptr_t) const MIJIN_NOEXCEPT { return empty(); } constexpr bool operator!=(std::nullptr_t) const MIJIN_NOEXCEPT { return !empty(); } constexpr operator bool() const MIJIN_NOEXCEPT { return !empty(); } constexpr bool operator!() const MIJIN_NOEXCEPT { return empty(); } constexpr pointer operator->() const MIJIN_NOEXCEPT { return get(); } constexpr element_type& operator*() const MIJIN_NOEXCEPT { return *get(); } [[nodiscard]] constexpr bool isOwning() const MIJIN_NOEXCEPT { return (mData & 1) == 1; } [[nodiscard]] constexpr bool empty() const MIJIN_NOEXCEPT { return mData == 0; } [[nodiscard]] constexpr pointer get() const MIJIN_NOEXCEPT { return std::bit_cast(mData & ~1); } constexpr void reset(pointer ptr, Owning owning) MIJIN_NOEXCEPT { if (isOwning()) { mDeleter(get()); } mData = std::bit_cast(ptr) | (owning ? 1 : 0); } constexpr void reset() MIJIN_NOEXCEPT { reset(nullptr, Owning::NO); } [[nodiscard]] pointer release() MIJIN_NOEXCEPT { return std::bit_cast(std::exchange(mData, 0) & ~1); } template TOtherDeleter> friend class DynamicPointer; }; template bool operator==(std::nullptr_t, const DynamicPointer& pointer) MIJIN_NOEXCEPT { return pointer == nullptr; } template bool operator!=(std::nullptr_t, const DynamicPointer& pointer) MIJIN_NOEXCEPT { return pointer != nullptr; } template DynamicPointer makeDynamic(TArgs&&... args) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v)) { return DynamicPointer(new T(std::forward(args)...), Owning::YES); } template TAllocator, typename... TArgs> DynamicPointer> makeDynamicWithAllocator(TAllocator allocator, TArgs&&... args) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v && std::is_nothrow_move_constructible_v)) { T* obj = allocator.allocate(1); if (obj != nullptr) { ::new(obj) T(std::forward(args)...); } return DynamicPointer>(obj, Owning::YES, AllocatorDeleter(std::move(allocator))); } template DynamicPointer wrapDynamic(T* raw) { return DynamicPointer(raw, Owning::NO); } } // namespace mijin #endif // !defined(MIJIN_MEMORY_DYNAMIC_POINTER_HPP_INCLUDED)