mijin2/source/mijin/memory/dynamic_pointer.hpp

193 lines
5.8 KiB
C++

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