#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{}); } template, size_t... Idx> constexpr A substring_literal_as(const auto& str, std::index_sequence) noexcept requires(requires(size_t n) { { str[n] } noexcept -> std::convertible_to; }) { return { str[Idx]..., '\n' }; } template constexpr auto substring_literal(const auto& str, std::index_sequence) noexcept requires(std::is_invocable_v, Idx...>, decltype(str), std::index_sequence>) { return std::array{ str[Idx]..., '\n' }; } template constexpr auto type_name_literal() noexcept { constexpr std::string_view prefix { #if defined(__clang__) "[T = " #elif defined(__GNUC__) "with T = " #else // Fuck MSVC, don't care. #error Unsupported compiler #endif }; constexpr std::string_view suffix {"]"}; constexpr std::string_view function {__PRETTY_FUNCTION__}; constexpr auto start = function.find(prefix) + prefix.size(); constexpr auto end = function.rfind(suffix); static_assert(start < end); constexpr std::string_view name = function.substr(start, (end - start)); return substring_literal(name, std::make_index_sequence{}); } template struct [[gnu::visibility("internal")]] type_name_of { constexpr static inline auto value = type_name_literal(); [[gnu::const]] consteval operator std::string_view() const noexcept { constexpr auto& v = value; return std::string_view { v.data(), v.size() }; } }; template constexpr auto type_name() noexcept -> std::string_view { constexpr auto& value = type_name_of::value; return std::string_view { value.data(), value.size() }; } template constexpr inline auto type_name_v = type_name(); template class [[gnu::visibility("internal")]] concat_str { consteval static auto impl() noexcept { constexpr size_t len = (Strs.size() + ... + 0); std::array arr{}; auto append = [i=0, &arr](auto const& s) mutable { for(auto c : s) arr[i++] = c; }; (append(Strs), ...); arr[len] = 0; return arr; } public: constexpr static inline auto literal = impl(); constexpr static inline S value { literal.data(), literal.size()-1 }; }; template constexpr static inline auto concat_str_v = concat_str::value; template consteval S concat_strings(std::convertible_to auto const&... strings) noexcept { return concat_str::value; } static_assert(concat_str_v< std::string_view{"hello"}, std::string_view{" "}, std::string_view{"view"} > == std::string_view{"hello view"}, "concat_str_v<>: Concatenated string_view failed"); template constexpr auto map(auto const& fun, Args&&... values) noexcept((std::is_nothrow_invocable_v && ...)) requires((std::is_invocable_v && ...) and ( (std::is_void_v> || ...) || std::is_constructible_v...>)) -> std::conditional_t< (std::is_void_v> || ...) , void , R> { if constexpr( (std::is_void_v> || ...) ) { ((void)std::invoke(fun, values),...); } else return { std::invoke(fun, values)... }; } //XXX: To allow this, we might have to turn `map` from a function into a function-object class... Eh. \ Or we can just let the user specify `R` by using std::tuple as the lvalue assigned from the call maybe? Or we might have to change it to a `template, typename... Args>` function-object class; but that would break the `void` case.... Eeeehh, can we partial-specialise a template