|
|
|
@ -13,9 +13,11 @@ namespace exopt::ptr {
|
|
|
|
|
using RawRef = T&;
|
|
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
|
using UniquePtr = T* __restrict__;
|
|
|
|
|
using UniquePtr = T __restrict__*;
|
|
|
|
|
template<typename T>
|
|
|
|
|
using UniqueRef = T& __restrict__;
|
|
|
|
|
using UniqueRef = T __restrict__&;
|
|
|
|
|
template<typename T>
|
|
|
|
|
using UniqueMoveRef = T __restrict__&&;
|
|
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
|
using AliasedPtr = T __attribute__((__may_alias__))*;
|
|
|
|
@ -136,6 +138,8 @@ namespace exopt::ptr {
|
|
|
|
|
constexpr virtual ~NullException() noexcept {}
|
|
|
|
|
|
|
|
|
|
constexpr std::string_view message() const noexcept /*override*/ { return "pointer was null"; }
|
|
|
|
|
|
|
|
|
|
std::runtime_error as_runtime() &&noexcept; /*TODO: override*/
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<typename T> requires(!std::is_reference_v<T>)
|
|
|
|
@ -158,7 +162,7 @@ namespace exopt::ptr {
|
|
|
|
|
constexpr ~NonNull() noexcept = default;
|
|
|
|
|
|
|
|
|
|
constexpr static NonNull<T> try_new(T* ptr) {
|
|
|
|
|
if(!ptr) _throw_null();
|
|
|
|
|
if(__builtin_expect(!ptr, false)) _throw_null();
|
|
|
|
|
return NonNull<T>{ptr};
|
|
|
|
|
}
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
@ -235,14 +239,95 @@ namespace exopt::ptr {
|
|
|
|
|
constexpr const T* operator->() const noexcept { return ptr_; }
|
|
|
|
|
private:
|
|
|
|
|
[[noreturn, gnu::noinline, gnu::cold]]
|
|
|
|
|
constexpr void _throw_null() {
|
|
|
|
|
constexpr static void _throw_null() {
|
|
|
|
|
if consteval {
|
|
|
|
|
throw NullException{};
|
|
|
|
|
} else {
|
|
|
|
|
throw std::runtime_error(NullException{}.message());
|
|
|
|
|
//std::terminate();
|
|
|
|
|
throw NullException{}.as_runtime();
|
|
|
|
|
//::exopt::util::throw_runtime(NullException{}); //XXX: Wut?
|
|
|
|
|
}
|
|
|
|
|
//throw error::as_runtime_error(error::comptime_fail<NullException>());
|
|
|
|
|
}
|
|
|
|
|
T* ptr_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// Holds a *unique reference* that cannot be aliased, and cannot be null.
|
|
|
|
|
template<typename T> requires(!std::is_reference_v<T>)
|
|
|
|
|
struct Unique {
|
|
|
|
|
using value_type = std::remove_cv_t<std::decay_t<T>>;
|
|
|
|
|
|
|
|
|
|
using reference_type = UniqueRef<value_type>;
|
|
|
|
|
using const_reference_type = UniqueRef<value_type const>;
|
|
|
|
|
using move_reference_type = UniqueMoveRef<value_type>;
|
|
|
|
|
|
|
|
|
|
using pointer_type = UniquePtr<value_type>;
|
|
|
|
|
using const_pointer_type = UniquePtr<value_type const>;
|
|
|
|
|
|
|
|
|
|
constexpr static inline bool is_mutable = !std::is_same_v<pointer_type, const_pointer_type>;
|
|
|
|
|
|
|
|
|
|
constexpr static Unique<T> try_new(pointer_type const&& ptr)
|
|
|
|
|
{
|
|
|
|
|
return { NonNull<T>::try_new(ptr) };
|
|
|
|
|
}
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
[[gnu::nonnull(1)]]
|
|
|
|
|
#endif
|
|
|
|
|
constexpr static Unique<T> new_unchecked(pointer_type ptr) noexcept {
|
|
|
|
|
#ifndef DEBUG
|
|
|
|
|
_EO_ASSUME(ptr!=nullptr);
|
|
|
|
|
#endif
|
|
|
|
|
return { NonNull<T>{ ptr } }; }
|
|
|
|
|
|
|
|
|
|
constexpr Unique(std::convertible_to<NonNull<value_type>> auto const&& ptr) noexcept
|
|
|
|
|
: m_ptr(ptr) {}
|
|
|
|
|
[[gnu::nonnull(1,2)]]
|
|
|
|
|
constexpr explicit Unique(T* __restrict__ ptr) noexcept
|
|
|
|
|
: m_ptr(ptr) {}
|
|
|
|
|
|
|
|
|
|
constexpr Unique(Unique&&) noexcept = default;
|
|
|
|
|
constexpr Unique& operator=(Unique&&) noexcept = default;
|
|
|
|
|
|
|
|
|
|
constexpr Unique(const Unique&) = delete;
|
|
|
|
|
constexpr Unique& operator=(const Unique&) = delete;
|
|
|
|
|
|
|
|
|
|
constexpr ~Unique() noexcept = default;
|
|
|
|
|
|
|
|
|
|
constexpr friend bool operator==(std::nullptr_t, const Unique& __restrict__) noexcept { return false; }
|
|
|
|
|
constexpr friend bool operator==(const Unique& __restrict__, std::nullptr_t) noexcept { return false; }
|
|
|
|
|
|
|
|
|
|
constexpr friend bool operator!=(std::nullptr_t, const Unique& __restrict__) noexcept { return true; }
|
|
|
|
|
constexpr friend bool operator!=(const Unique& __restrict__, std::nullptr_t) noexcept { return true; }
|
|
|
|
|
|
|
|
|
|
constexpr friend auto operator<=>(const Unique& __restrict__ a, const Unique& __restrict__ b) noexcept { return a.get() <=> b.get(); }
|
|
|
|
|
constexpr friend auto operator<=>(const Unique& __restrict__ a, T* __restrict__ b) noexcept { return (!b) ? (true<=>false) : (a.get() <=> b); }
|
|
|
|
|
constexpr friend auto operator<=>(T* __restrict__ a, const Unique& __restrict__ b) noexcept { return (!a) ? (false<=>true) : (a <=> b.get()); }
|
|
|
|
|
|
|
|
|
|
[[gnu::returns_nonnull]]
|
|
|
|
|
constexpr pointer_type get() const __restrict__ noexcept { return m_ptr.get(); }
|
|
|
|
|
|
|
|
|
|
[[gnu::returns_nonnull]]
|
|
|
|
|
constexpr operator pointer_type() const __restrict__ noexcept { return get(); }
|
|
|
|
|
constexpr explicit operator NonNull<T>() const noexcept { return m_ptr; }
|
|
|
|
|
|
|
|
|
|
constexpr reference_type operator*() __restrict__ & noexcept { return *get(); }
|
|
|
|
|
constexpr const_reference_type operator*() const __restrict__& noexcept { return *get(); }
|
|
|
|
|
constexpr move_reference_type operator*() __restrict__ &&noexcept { return std::move(*get()); }
|
|
|
|
|
constexpr decltype(auto) operator*() const __restrict__ &&noexcept { return *get(); }
|
|
|
|
|
|
|
|
|
|
[[gnu::returns_nonnull]]
|
|
|
|
|
constexpr pointer_type operator->() __restrict__ noexcept { return get(); }
|
|
|
|
|
[[gnu::returns_nonnull]]
|
|
|
|
|
constexpr const_pointer_type operator->() const __restrict__ noexcept { return get(); }
|
|
|
|
|
private:
|
|
|
|
|
NonNull<T> m_ptr;
|
|
|
|
|
};
|
|
|
|
|
#if 0
|
|
|
|
|
consteval bool uniq_check() noexcept {
|
|
|
|
|
int ptr = 0;
|
|
|
|
|
int* const ptr0 = &ptr;
|
|
|
|
|
return Unique<int>::try_new(std::move(ptr0)).get() == &ptr;
|
|
|
|
|
}
|
|
|
|
|
static_assert(uniq_check(), "Invalid Unique<>.get()");
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|