You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
6.2 KiB
168 lines
6.2 KiB
|
|
#pragma once
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "pointer.h"
|
|
#include "util.hh"
|
|
|
|
#include "exopt.h"
|
|
|
|
namespace exopt::types { namespace boxed {
|
|
template<typename T>
|
|
struct [[gnu::visibility("internal")]] boxable_value_type { using type = std::conditional_t
|
|
<std::is_reference_v<T>
|
|
,std::reference_wrapper<std::remove_cvref_t<T> >
|
|
,T>; };
|
|
|
|
template<typename T>
|
|
constexpr std::unique_ptr<T> uniq_clone(std::unique_ptr<T> const& c) noexcept(std::is_nothrow_copy_constructible_v<T>) requires(std::is_copy_constructible_v<T>)
|
|
{ if(__builtin_expect(bool(c), true)) return std::make_unique<T>(*c); return nullptr; }
|
|
|
|
template<typename T>
|
|
constexpr std::unique_ptr<T> uniq_clone_unsafe(std::unique_ptr<T> const& c)
|
|
#ifndef DEBUG
|
|
noexcept(std::is_nothrow_copy_constructible_v<T>)
|
|
#endif
|
|
requires(std::is_copy_constructible_v<T>)
|
|
{ if(__builtin_expect(!bool(c), false))
|
|
#ifdef DEBUG
|
|
throw ptr::NullException{};
|
|
#else
|
|
__builtin_unreachable();
|
|
#endif
|
|
return std::make_unique<T>(*c); }
|
|
|
|
template<typename From, typename To>
|
|
concept binary_convertible = std::is_convertible_v<From, To> and std::is_convertible_v<To, From>;
|
|
|
|
template<typename T>
|
|
struct Box {
|
|
typedef boxable_value_type<T>::type type;
|
|
|
|
template<std::derived_from<T> U, bool RuntimeCheck=false>
|
|
constexpr static Box<T> new_dynamic(U&& v) noexcept(!RuntimeCheck) {
|
|
if constexpr(!RuntimeCheck) {
|
|
auto boxed = std::make_unique<U>(std::move(v));
|
|
return Box<T>{std::unique_ptr<T>{static_cast<T*>(boxed.release())}};
|
|
} else
|
|
return Box<T>{std::make_unique<T>(dynamic_cast<T&&>(std::move(v)))};
|
|
}
|
|
|
|
constexpr Box() noexcept(std::is_nothrow_default_constructible_v<T>) requires(std::is_default_constructible_v<T>)
|
|
: m_ptr{std::make_unique<T>()} {}
|
|
|
|
template<typename... Args> requires(std::is_constructible_v<T, Args...>)
|
|
constexpr Box(Args&&... ctor) noexcept(std::is_nothrow_constructible_v<T, Args...>)
|
|
: m_ptr{std::make_unique<T>(std::forward<Args>(ctor)...)} {}
|
|
|
|
constexpr Box(T&& value) noexcept(std::is_nothrow_constructible_v<T>)
|
|
: m_ptr{std::make_unique<T>(std::move(value))} {}
|
|
|
|
constexpr Box(Box&&) noexcept = default;
|
|
constexpr Box& operator=(Box&&) noexcept = default;
|
|
constexpr Box(const Box& copy) noexcept(std::is_nothrow_copy_constructible_v<T>) requires(std::is_copy_constructible_v<T>)
|
|
: m_ptr{uniq_clone_unsafe(copy.m_ptr)} {}
|
|
constexpr Box& operator=(Box const& copy) noexcept(std::is_nothrow_copy_assignable_v<T>) requires(std::is_copy_assignable_v<T>)
|
|
{
|
|
if(__builtin_expect(this != std::addressof(copy), true)) {
|
|
m_ptr = uniq_clone_unsafe(copy.m_ptr);
|
|
} return *this;
|
|
}
|
|
|
|
[[gnu::nonnull(1)]]
|
|
constexpr static Box<T> from_raw_ptr(T* ptr) noexcept {
|
|
return Box<T>{ std::unique_ptr<T>{ ptr } };
|
|
}
|
|
constexpr static Box<T> from_raw_ptr(std::unique_ptr<T>&& uniq)
|
|
#if DEBUG
|
|
{ if(__builtin_expect(!uniq, false)) throw ptr::NullException{};
|
|
#else
|
|
|
|
noexcept {
|
|
_EO_ASSUME(uniq.get() != nullptr);
|
|
#endif
|
|
return Box<T>{ std::move(uniq) };
|
|
}
|
|
[[gnu::returns_nonnull]]
|
|
constexpr T* get() const noexcept { return m_ptr.get(); }
|
|
[[gnu::returns_nonnull]]
|
|
constexpr T* release() noexcept { return m_ptr.release(); }
|
|
|
|
constexpr std::unique_ptr<T>&& into_unique() &&noexcept { return std::move(m_ptr); }
|
|
|
|
//TODO: Accessors, `(explicit?) operator std::unique_ptr<T> const&[&]`?, etc.
|
|
|
|
constexpr ~Box() = default;
|
|
private:
|
|
constexpr explicit Box(std::unique_ptr<T>&& ptr)
|
|
: m_ptr(std::move(ptr)) {
|
|
_EO_ASSUME(m_ptr.get());
|
|
}
|
|
|
|
std::unique_ptr<T> m_ptr; // Unique<T> m_ptr;
|
|
};
|
|
|
|
#define _EO_ADD_RV std::add_rvalue_reference_v
|
|
|
|
template<typename Base, typename T = Base>
|
|
constexpr inline bool is_boxable_v = binary_convertible<_EO_ADD_RV<T>, _EO_ADD_RV<Base> > and requires(T&& value) {
|
|
typename Box<Base>;
|
|
typename Box<Base>::type;
|
|
{ std::make_unique<T>(std::move(value)) } -> std::same_as<std::unique_ptr<T>>;
|
|
};
|
|
template<typename T> concept is_boxable = is_boxable_v<T>;
|
|
|
|
template<typename T>
|
|
constexpr inline bool is_nothrow_boxable_v = is_boxable_v<T> && std::is_nothrow_constructible_v<Box<T>, T>;
|
|
|
|
#undef _EO_ADD_RV
|
|
|
|
template<typename T, typename U> requires(requires(T&& o) { static_cast<U&&>(o); })
|
|
constexpr Box<U> static_box_cast(Box<T>&& b) noexcept { return Box<U>::from_raw_ptr(static_cast<U*>(b.release())); }
|
|
|
|
template<typename T, typename U> requires(requires(T&& o) { static_cast<U&&>(o); })
|
|
constexpr Box<U> dynamic_box_cast(Box<T>&& b) {
|
|
auto* rel = b.release();
|
|
try {
|
|
return Box<U>::from_raw_ptr(std::addressof(dynamic_cast<U&&>(std::move(*rel))));
|
|
} catch(...) {
|
|
delete rel;
|
|
throw;
|
|
}
|
|
} //TODO: Overload for `const&` that does the type check *before* the copy allocation.
|
|
|
|
template<typename T, typename R /*XXX: Maybe remove this, and put the requires after the definition, using `auto&& value` instead of `R&& value`*/> requires(std::derived_from<R, T>)
|
|
constexpr Box<T> box(R&& value, util::comptime<bool> auto Check = _EO_CONSTANT_VALUE(false)) noexcept(std::is_nothrow_invocable_v< Box<T>::new_dynamic<R, bool(Check)>) //XXX: This seems illegal...
|
|
{ return Box<T>::template new_dynamic<R, Check>(std::move(value)); }
|
|
|
|
template<typename T>
|
|
constexpr Box<T> box(std::convertible_to<T> auto&& value) noexcept(std::is_nothrow_constructible_v<Box<T>, std::add_rvalue_reference_v<T>>)
|
|
{
|
|
/*if constexpr(requires(decltype(value) v) { static_cast<T&&>(std::move(v)); }) {
|
|
return Box<T>::template new_dynamic<T>(std::move(value));
|
|
} else */return { static_cast<T>(std::move(value)) };
|
|
}
|
|
template<typename T>
|
|
constexpr Box<T> box(T&& value) noexcept(std::is_nothrow_constructible_v<Box<T>, T>)
|
|
{ return { std::move(value) }; }
|
|
}
|
|
using boxed::Box;
|
|
}
|
|
|
|
namespace exopt::types { namespace optional [[gnu::visibility("internal")]] {
|
|
template<typename T>
|
|
struct null_optimise<boxed::Box<T> > { constexpr static inline bool value = true;
|
|
//XXX: Eh.. Idk if Option<T>'s lifetime design can make this work...
|
|
using held_type = boxed::Box<T>;
|
|
using type = boxed::Box<T>&;
|
|
constexpr static decltype(auto) convert_to_held(std::add_rvalue_reference_t<type> t) noexcept { return std::move(t); }
|
|
constexpr static decltype(auto) convert_from_held(std::add_rvalue_reference_t<held_type> t) noexcept { return std::forward<type>(t); }
|
|
|
|
constexpr static inline T* sentinel = nullptr;
|
|
};
|
|
|
|
|
|
}
|