#pragma once #include "borrowed-ptr.hpp" #include "compat/assert.hpp" #include #define FM_BPTR_DEBUG #ifdef __CLION_IDE__ #define fm_bptr_assert(...) (void(__VA_ARGS__)) #elif defined FM_BPTR_DEBUG && !defined FM_NO_DEBUG #define fm_bptr_assert(...) fm_assert(__VA_ARGS__) #else #define fm_bptr_assert(...) void() #endif #ifdef __GNUG__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" #endif namespace floormat::detail_borrowed_ptr { #ifdef __GNUG__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" #endif struct control_block { void* _ptr; // todo maybe add directly embeddable objects? uint32_t _count; virtual void free_ptr() noexcept = 0; static void decrement(control_block*& blk) noexcept; }; #ifdef __GNUG__ #pragma GCC diagnostic pop #endif template struct control_block_impl final: control_block { void free_ptr() noexcept override; [[nodiscard]] static control_block* create(T* ptr) noexcept; protected: explicit control_block_impl(T* ptr) noexcept; }; template control_block_impl::control_block_impl(T* ptr) noexcept { fm_bptr_assert(ptr); _ptr = ptr; _count = 1; } template void control_block_impl::free_ptr() noexcept { delete static_cast(_ptr); } template control_block* control_block_impl::create(T* ptr) noexcept { if (ptr) { auto* __restrict ret = new control_block_impl{ptr}; return ret; } else return nullptr; } } // namespace floormat::detail_borrowed_ptr namespace floormat { template bptr::bptr(DirectInitT, T* casted_ptr, detail_borrowed_ptr::control_block* blk) noexcept: casted_ptr{casted_ptr}, blk{blk} {} template template requires std::is_constructible_v bptr::bptr(InPlaceInitT, Ts&&... args) noexcept: bptr{ new T{ forward(args...) } } { } template bptr::bptr(std::nullptr_t) noexcept: casted_ptr{nullptr}, blk{nullptr} {} template bptr::bptr() noexcept: bptr{nullptr} {} template bptr::bptr(T* ptr) noexcept: casted_ptr{ptr}, blk{detail_borrowed_ptr::control_block_impl::create(ptr)} { fm_bptr_assert(!blk || blk->_count == 1 && blk->_ptr); } template bptr::~bptr() noexcept { if (blk) blk->decrement(blk); } template bptr::bptr(const bptr& other) noexcept: bptr{other, private_tag} {} template bptr& bptr::operator=(const bptr& other) noexcept { return _copy_assign(other); } template template Y> bptr::bptr(const bptr& other) noexcept: bptr{other, private_tag} {} template template Y> bptr& bptr::operator=(const bptr& other) noexcept { return _copy_assign(other); } template bptr& bptr::operator=(bptr&& other) noexcept { return _move_assign(move(other)); } template bptr::bptr(bptr&& other) noexcept: bptr{move(other), private_tag} {} template template Y> bptr::bptr(bptr&& other) noexcept: bptr{move(other), private_tag} {} template template Y> bptr& bptr::operator=(bptr&& other) noexcept { return _move_assign(move(other)); } template void bptr::reset() noexcept { if (blk) { fm_bptr_assert(casted_ptr); blk->decrement(blk); casted_ptr = nullptr; blk = nullptr; } } template template void bptr::destroy() noexcept { if constexpr(MaybeEmpty) if (!blk) return; blk->free_ptr(); blk->_ptr = nullptr; casted_ptr = nullptr; } template bptr& bptr::operator=(std::nullptr_t) noexcept { reset(); return *this; } template template bptr::bptr(const bptr& other, private_tag_t) noexcept: casted_ptr{other.casted_ptr}, blk{other.blk} { if (blk) { ++blk->_count; fm_bptr_assert(blk->_count > 1); } } template template bptr& bptr::_copy_assign(const bptr& other) noexcept { if (blk != other.blk) { CORRADE_ASSUME(this != &other); // todo! see if helps if (blk) blk->decrement(blk); casted_ptr = other.casted_ptr; blk = other.blk; if (blk) ++blk->_count; } return *this; } template template bptr::bptr(bptr&& other, private_tag_t) noexcept: casted_ptr{other.casted_ptr}, blk{other.blk} { other.casted_ptr = nullptr; other.blk = nullptr; } template template bptr& bptr::_move_assign(bptr&& other) noexcept { if (blk) blk->decrement(blk); casted_ptr = other.casted_ptr; blk = other.blk; other.casted_ptr = nullptr; other.blk = nullptr; return *this; } template T* bptr::get() const noexcept { if (blk && blk->_ptr) [[likely]] { fm_bptr_assert(casted_ptr); return casted_ptr; } else return nullptr; } template T* bptr::operator->() const noexcept { auto* ret = get(); fm_bptr_assert(ret); return ret; } template T& bptr::operator*() const noexcept { return *operator->(); } template bool operator==(const bptr& a, const bptr& b) noexcept { return a.get() == b.get(); } template bptr::operator bool() const noexcept { return get(); } template void bptr::swap(bptr& other) noexcept { using floormat::swap; swap(casted_ptr, other.casted_ptr); swap(blk, other.blk); } template uint32_t bptr::use_count() const noexcept { if (blk && blk->_ptr) [[likely]] return blk->_count; else return 0; } } // namespace floormat #ifdef __GNUG__ #pragma GCC diagnostic pop #endif