#pragma once #include #include #include #include #include "exopt.h" #include "optional.h" #define _EO_CONSTANT_VALUE(X) ([]() { \ struct { \ typedef decltype(X) comptime_constant_t; \ constexpr static inline auto VALUE = (X); \ consteval operator comptime_constant_t() const noexcept { return (X); } \ } inner; \ return inner; \ })() namespace exopt { namespace util [[gnu::visibility("internal")]] { template concept comptime = std::convertible_to and requires{ typename T::comptime_constant_t; }; template concept is_complete = requires{ { sizeof(T) == sizeof(T) }; }; constexpr auto comptime_value(auto value) noexcept { using U = decltype(value); struct inner final { typedef U comptime_constant_t; consteval operator comptime_constant_t() const noexcept { return std::move(value); } U value; constexpr inner(std::add_rvalue_reference_t m) noexcept : value(std::move(m)) {} constexpr ~inner() = default; }; return inner{ std::move(value) }; } template requires(comptime) consteval T comptime_eval() noexcept { if constexpr(requires { { decltype(value)::VALUE } -> std::convertible_to; }) { return decltype(value)::VALUE; } else return value; } template requires(comptime) constexpr inline T comptime_eval_v = comptime_eval(); template consteval T comptime_eval(comptime auto value) noexcept { return comptime_eval(); } static_assert(comptime_eval(comptime_value(5.0)) == 5.0, "Bad comptime_eval() consteval"); static_assert(comptime_eval(_EO_CONSTANT_VALUE(3.f)) == 3.f, "Bad comptime_eval() macro"); static_assert(std::is_same_v, "Bad comptime_eval() return type consteval"); static_assert(std::is_same_v, "Bad comptime_eval() return type macro"); static_assert(comptime_value(std::string_view{"hello"}.size()) == 5, "Bad comptime_value()"); static_assert(_EO_CONSTANT_VALUE(std::string_view{"hello world"}) == std::string_view{"hello world"}, "Bad CONSTANT_VALUE()"); template, typename _I = std::make_index_sequence> constexpr auto array_literal(const T (&a)[N]) noexcept -> Array { constexpr decltype(auto) _array_create = [](auto const& a, std::index_sequence) noexcept { return Array { a[I]... }; }; return _array_create(std::move(a), _I{}); } template, typename _I = std::make_index_sequence> constexpr auto array_literal(T (&&a)[N]) noexcept -> Array { constexpr decltype(auto) _array_create = [](auto&& a, std::index_sequence) noexcept { return Array { std::move(a[I])... }; }; return _array_create(std::move(a), _I{}); } struct [[gnu::visibility("internal")]] CTInternalFatalError { constexpr CTInternalFatalError() noexcept = default; constexpr CTInternalFatalError(CTInternalFatalError const&) noexcept = default; constexpr CTInternalFatalError(CTInternalFatalError &&) noexcept = default; constexpr CTInternalFatalError& operator=(CTInternalFatalError const&) noexcept = default; constexpr CTInternalFatalError& operator=(CTInternalFatalError&&) noexcept = default; constexpr virtual ~CTInternalFatalError() noexcept = default; constexpr operator std::string_view() const noexcept { return message(); } consteval virtual std::string_view message() const noexcept =0; }; [[noreturn, gnu::noinline, gnu::cold]] void throw_runtime(std::string_view&&); [[noreturn]]//, gnu::noinline, gnu::cold]] constexpr void throw_runtime(std::convertible_to auto&& msg) { std::string_view view{msg}; if consteval { using CTE = CTInternalFatalError; struct CTIFRuntimeError : public CTE { using CTE::CTE; consteval CTIFRuntimeError(std::string_view&& view) noexcept : CTE(), m_message(std::move(view)) {} consteval std::string_view message() const noexcept override { return m_message; } constexpr virtual ~CTIFRuntimeError() noexcept {} private: std::string_view m_message; }; throw CTIFRuntimeError(std::move(view)); } else { throw_runtime(std::move(view)); } __builtin_unreachable(); } // Returns an anonymous union with inavtive field: `T value` and active field `assume init` (no unique address). template requires(!std::is_reference_v) constexpr auto uninit() noexcept { union UI { struct assume{}; T value; [[no_unique_address]] assume init{}; }; return UI{}; } template requires(!std::is_reference_v) struct alignas(T) maybe_uninit { using uninit = std::array; constexpr maybe_uninit() noexcept {} constexpr maybe_uninit(const maybe_uninit& c) noexcept : m_uninit(c.m_uninit) {} constexpr maybe_uninit(maybe_uninit&& m) noexcept : m_uninit(std::move(m.m_uninit)) {} constexpr maybe_uninit& operator=(maybe_uninit const& c) noexcept { m_uninit = c.m_uninit; return *this; } constexpr maybe_uninit& operator=(maybe_uninit&& c) noexcept { m_uninit = std::move(c.m_uninit); return *this; } constexpr T& operator=(T&& value) noexcept { m_init = std::move(value); return *this; } constexpr T& operator=(T const& copy) noexcept { m_init = maybe_uninit{copy}; return *this; //XXX: These assignment operators may not be a good idea, we can't guarantee which active destriminant it is. } constexpr T& assume_init() & noexcept = delete; constexpr T&& assume_init() && noexcept { return std::move(m_init); } constexpr T const& assume_init() const& noexcept { return m_init; } constexpr T const&& assume_init() const&& noexcept { return m_init; } constexpr uninit& data() &noexcept { return m_uninit; } constexpr uninit&& data() &&noexcept { return std::move(m_uninit); } constexpr uninit const& data() const&noexcept { return m_uninit; } constexpr uninit const&& data() const&& noexcept { return std::move(m_uninit); } constexpr T* get() noexcept { return std::addressof(m_init); } constexpr const T* get() const noexcept { return std::addressof(m_init); } //TODO: accessors, `assume_init_*()`, etc. constexpr ~maybe_uninit() noexcept {} constexpr void assume_init_drop() noexcept(std::is_nothrow_destructible_v) { m_init.~T(); m_uninit = {}; } [[nodiscard]] constexpr T assume_init_take() noexcept(std::is_nothrow_move_constructible_v) { auto v = std::move(m_init); assume_init_drop(); return v; } constexpr maybe_uninit init(T&& value) noexcept(std::is_nothrow_move_constructible_v) { return maybe_uninit{std::move(value), EXPLICIT_INIT_TAG{}}; } template requires(std::is_constructible_v) constexpr maybe_uninit init_in_place(Args&&... args) noexcept(std::is_nothrow_constructible_v) { return maybe_uninit{EXPLICIT_INIT_TAG{}, std::forward(args)...}; } private: enum EXPLICIT_INIT_TYPE { EXPLICIT_INIT_IN_PLACE, EXPLICIT_INIT_MOVE, }; template struct EXPLICIT_INIT_TAG {}; constexpr maybe_uninit(T&& value, EXPLICIT_INIT_TAG) noexcept(std::is_nothrow_move_constructible_v) : m_init{std::move(value)} {} template requires(std::is_constructible_v) constexpr maybe_uninit(EXPLICIT_INIT_TAG _a, Args&&... args) noexcept(std::is_nothrow_constructible_v) : m_init{std::forward(args)...} { (void) _a; } union { T m_init; [[no_unique_address]] alignas(T) uninit m_uninit{}; }; }; namespace { constexpr std::string_view ui_test() noexcept { auto ui = uninit(); maybe_uninit mi; ui.init.~assume(); ui.value = std::string_view{"Hello"}; return std::move(ui.value); } static_assert(ui_test()[0] == 'H', "Bad uninit()"); } } }