177 lines
5.2 KiB
C++
177 lines
5.2 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 = std::default_delete<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_constructible_v<TDeleter, TOtherDeleter&&>)
|
|
constexpr DynamicPointer(DynamicPointer<TOther, TOtherDeleter>&& other) MIJIN_NOEXCEPT_IF((std::is_nothrow_convertible_v<TOtherDeleter, TDeleter>))
|
|
: mData(std::exchange(other.mData, 0)), mDeleter(std::move(other.mDeleter)) {
|
|
MIJIN_ASSERT(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::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, std::default_delete<T>> makeDynamic(TArgs&&... args) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<T, TArgs...>))
|
|
{
|
|
return DynamicPointer<T, std::default_delete<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)));
|
|
}
|
|
} // namespace mijin
|
|
|
|
#endif // !defined(MIJIN_MEMORY_DYNAMIC_POINTER_HPP_INCLUDED)
|