@ -12,6 +12,7 @@
# 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 ; \
@ -28,7 +29,7 @@ namespace exopt { namespace util [[gnu::visibility("internal")]] {
constexpr auto comptime_value ( auto value ) noexcept {
using U = decltype ( value ) ;
struct inner {
struct inner final {
typedef U comptime_constant_t ;
consteval operator comptime_constant_t ( ) const noexcept { return std : : move ( value ) ; }
U value ;
@ -38,11 +39,41 @@ namespace exopt { namespace util [[gnu::visibility("internal")]] {
} ;
return inner { std : : move ( value ) } ;
}
template < typename T , auto value > requires ( comptime < decltype ( value ) , T > )
consteval T comptime_eval ( ) noexcept {
if constexpr ( requires {
{ decltype ( value ) : : VALUE } - > std : : convertible_to < decltype ( value ) : : comptime_constant_t > ;
} ) {
return decltype ( value ) : : VALUE ;
} else return value ;
}
template < typename T , auto value > requires ( comptime < decltype ( value ) , T > )
constexpr inline T comptime_eval_v = comptime_eval < T , value > ( ) ;
template < typename T >
consteval T comptime_eval ( comptime < T > auto value ) noexcept {
return comptime_eval < T , value > ( ) ;
}
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 < decltype ( comptime_eval ( comptime_value ( 4.f ) ) ) , float > , " Bad comptime_eval() return type consteval " ) ;
static_assert ( std : : is_same_v < decltype ( comptime_eval ( _EO_CONSTANT_VALUE ( 4.0 ) ) ) , double > , " 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 T , typename U >
concept shares_layout = sizeof ( T ) = = sizeof ( U ) & & alignof ( T ) = = alignof ( U ) ;
template < typename T , typename U >
concept exact_type = std : : is_same_v < T , U > ;
template < typename T , size_t N , typename Array = std : : array < T , N > , typename _I = std : : make_index_sequence < N > >
constexpr auto array_literal ( const T ( & a ) [ N ] ) noexcept
- > Array
@ -64,6 +95,201 @@ namespace exopt { namespace util [[gnu::visibility("internal")]] {
return _array_create ( std : : move ( a ) , _I { } ) ;
}
template < size_t N , typename A = std : : array < char , N > , size_t . . . Idx >
constexpr A substring_literal_as ( const auto & str , std : : index_sequence < Idx . . . > ) noexcept
requires ( requires ( size_t n ) {
{ str [ n ] } noexcept - > std : : convertible_to < char > ;
} )
{
return { str [ Idx ] . . . , ' \n ' } ;
}
template < size_t . . . Idx >
constexpr auto substring_literal ( const auto & str , std : : index_sequence < Idx . . . > ) noexcept
requires ( std : : is_invocable_v < substring_literal_as < sizeof . . . ( Idx ) , std : : array < char , sizeof . . . ( Idx ) > , Idx . . . > , decltype ( str ) , std : : index_sequence < Idx . . . > > )
{ return std : : array { str [ Idx ] . . . , ' \n ' } ; }
template < typename T >
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 < name . size ( ) > { } ) ;
}
template < typename T >
struct [[gnu::visibility("internal")]] type_name_of {
constexpr static inline auto value = type_name_literal < T > ( ) ;
[[gnu::const]]
consteval operator std : : string_view ( ) const noexcept {
constexpr auto & v = value ;
return std : : string_view { v . data ( ) , v . size ( ) } ;
}
} ;
template < typename T >
constexpr auto type_name ( ) noexcept - > std : : string_view
{
constexpr auto & value = type_name_of < T > : : value ;
return std : : string_view { value . data ( ) , value . size ( ) } ;
}
template < typename T >
constexpr inline auto type_name_v = type_name < T > ( ) ;
template < typename S = std : : string_view , S const & . . . Strs >
class [[gnu::visibility("internal")]] concat_str {
consteval static auto impl ( ) noexcept
{
constexpr size_t len = ( Strs . size ( ) + . . . + 0 ) ;
std : : array < char , len + 1 > 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 < std : : string_view const & . . . Ss >
constexpr static inline auto concat_str_v = concat_str < std : : string_view , Ss . . . > : : value ;
template < typename S = std : : string_view >
consteval S concat_strings ( std : : convertible_to < S > auto const & . . . strings ) noexcept
{
return concat_str < S , S { strings } . . . > : : 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 < template < typename > P , typename . . . Values >
struct _EO ( internal ) is_all_match {
constexpr static inline auto value = ( P < Values > & & true & & . . . ) ;
consteval operator auto ( ) const noexcept { return value ; }
} ;
template < template < typename > P , typename . . . Values >
struct _EO ( internal ) is_any_match {
constexpr static inline auto value = ( P < Values > | | false | | . . . ) ;
consteval operator auto ( ) const noexcept { return value ; }
} ;
//XXX: Idk if template<template<s are allowed in this context...
template < template < typename > P , typename . . . Values > requires ( requires { typename is_all_match < P , Values > ; } )
constexpr inline auto is_all_match_v = is_all_match < P , Values . . . > : : value ;
template < template < typename > P , typename . . . Values > requires ( requires { typename is_any_match < P , Values > ; } )
constexpr inline auto is_any_match_v = is_any_match < P , Values . . . > : : value ;
template < auto . . . Values >
struct _EO ( internal ) is_all {
constexpr static inline auto value = ( Values & & true & & . . . ) ;
} ;
template < auto . . . Values >
struct _EO ( internal ) is_any {
constexpr static inline auto value = ( Values | | false | | . . . ) ;
} ;
template < auto . . . Values >
constexpr inline auto is_all_v = is_all < Values . . . > : : value ;
template < auto . . . Values >
constexpr inline auto is_any_v = is_any < Values . . . > : : value ;
template < typename . . . Args >
constexpr inline bool is_empty_v = sizeof . . . ( Args ) = = 0 ;
template < auto . . . Values > // auto and typename aren't compatible unfortunately
constexpr inline bool is_empty_vv = sizeof . . . ( Values ) = = 0 ;
// Convenience immediate function overloads for checking if a parameter pack *or* a value pack is empty without different names.
template < typename . . . A >
consteval bool is_empty_pack ( ) noexcept { return sizeof . . . ( A ) = = 0 ; }
template < auto . . . A >
consteval bool is_empty_pack ( ) noexcept { return sizeof . . . ( A ) = = 0 ; }
/// Deducable version of `is_empty_pack<typename...>()`
///
/// Can be used as `is_empty_pack(pack...)` instead of the slightly more ugly `is_empty_pack<decltype(pack)...>()` though with (very unlikely, but possible) implications of actually "evaluating" the pack (despite the values themselves are `const&` and never used, they're still passed.)
/// HOWEVER: The latter should likely be preferred, since this function takes possible-nonconstexpr types as arguments, it cannot be immediate like the non-argument taking overload. See below comment for more information on that.
template < typename . . . A >
[[gnu::const]] //TODO: XXX: This mostly useless function won't mess up overload resolution for `is_empty_pack<types...>()`, right? If it will, either find a way to make sure it comes LAST, or remove it, it's not worth the hassle just so users (i.e. me) can avoid typeing `decltype` for once in my fuckin life... what a stupud keyword name srsl (see static_assert() below.)
constexpr auto is_empty_pack ( A const & . . . ) noexcept { return is_empty_pack < A . . . > ( ) ; } / / This overload exists for : e . g usage for index_sequence : \
` template < size_t N > f ( const auto ( & v ) [ N ] ) - > \
template < size_t Is . . . > _f ( auto const & v , std : : index_sequence < Is . . . > ) ; ` \
Can be used like : \
_f ( const auto & v , std : : convertible_to < size_t > auto & & . . . is ) \
{ if constexpr ( is_empty_pack ( is . . . ) ) { . . . } . . . } ` \
i . e : for unknown - typed non - type parameter packs ` auto . . . ` s packs can be ` forward < decltype ( pack ) > ( pack ) . . . ` ' d ( or just passed , as the taken value is always ` const & ` ) to is_empty_pack ( . . . ) and ` A . . . ` can therefor be deduced . This is not possible for the non - value taking overloads , and also must be non - consteval since any ` A ` might not be constexpr . \
\
It is a convenience function that is essentially equivelent to ` is_empty_pack < decltype ( pack ) . . . > ( ) ` , which should be preferred , since it is consteval and will always be guaranteed to be consteval regardless of the contents of ` pack ` itself , but I highly doubt any compiler will actually generate any argument - passing code for this overload either .
static_assert ( ! is_empty_pack < int , long , float > ( )
and is_empty_pack < > ( ) //XXX: This might also be ambiguous between the type and value one... Eh.... That's actually useful though, so removing this explicit (and pointless) *usage* of the function from the test would be fine if they don't clash in other ways too.
and ! is_empty_pack < 0 , 1 , 2 > ( )
and ! is_empty_pack ( 0u , 1.0 , 2l )
and is_empty_pack ( ) , " `is_empty_pack()` overload issues. " ) ;
template < typename R , typename . . . Args >
constexpr auto map ( auto const & fun , Args & & . . . values ) noexcept ( is_empty_v < Args . . . > or is_all_v < std : : is_nothrow_invocable_v < decltype ( fun ) , Args > . . . > )
requires ( is_empty_v < Args . . . > or ( is_all_v < std : : is_invocable_v < decltype ( fun ) , Args > . . . > and (
is_any_v < std : : is_void_v < std : : invoke_result_t < decltype ( fun ) , Args > > . . . > | |
std : : is_constructible_v < R , std : : invoke_result_t < decltype ( fun ) , Args > . . . > ) ) )
- > std : : conditional_t < is_empty_v < Args . . . > | | is_any_v < std : : is_void_v < std : : invoke_result_t < decltype ( fun ) , Args > > . . . >
, std : : void_t < std : : invoke_result_t < decltype ( fun ) , Args > . . . >
, R >
{
if constexpr ( is_any_v < std : : is_void_v < std : : invoke_result_t < decltype ( fun ) , Args > > . . . > ) {
( ( void ) std : : invoke ( fun , values ) , . . . ) ;
else if constexpr ( sizeof . . . ( Args ) = = 0 )
( void ) 0 ;
} 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 < template < typename . . . Results > , typename . . . Args > ` function - object class ; but that would break the ` void ` case . . . . Eeeehh , can we partial - specialise a template < template into a template < typename ? I don ' t think we can on the surface without helper ' ' trait ' ' templates . . . . Eh . . . . . .
template < typename . . . Args >
using map_tuple = map < std : : tuple < Args . . . > , Args . . . > ;
/ / TODO : template < is_valid_metafunction_with_two_unknown_type_arguments_and_a_possibly_variable_return_type < R ( T1 , T2 ) > Op , typename . . . Args > \
auto reduce ( auto const & fun , Op const & comb , Args & & . . . values ) noexcept ( . . . ) \
requires ( ? ? ? ) - > std : : invoke_result_t < ? ? ? ? ? ? > { / / TODO : Reframe the call to ` comb ` as an operator overload in a shim class , and use binary fold to make the call , like : ` \
shim s { comb } ; return ( s + std : : invoke ( fun , values ) + . . . ) ; } `
template < typename Fn , typename . . . Args > requires ( ( std : : is_invocable_v < Fn , Args > & & . . . ) )
constexpr void apply ( const Fn & fun , Args & & . . . values ) noexcept ( ( std : : is_nothrow_invocable_v < Fn , Args > & & . . . ) )
/*-> std::conditional_t< (std::is_void_v<std::invoke_result_t<Fn, Args>> || ...)
, void
, std : : common_type_t < std : : invoke_result_t < Fn , Args > . . . > > */
{ map < void , Args . . . > ( fun , std : : forward < Args > ( values ) . . . ) ; }
struct [[gnu::visibility("internal")]] CTInternalFatalError {
constexpr CTInternalFatalError ( ) noexcept = default ;
constexpr CTInternalFatalError ( CTInternalFatalError const & ) noexcept = default ;
@ -77,29 +303,33 @@ namespace exopt { namespace util [[gnu::visibility("internal")]] {
consteval virtual std : : string_view message ( ) const noexcept = 0 ;
} ;
/// Throw a string at runtime, as an `std::runtime_error`.
[[noreturn, gnu::noinline, gnu::cold]]
void throw_runtime ( std : : string_view & & ) ;
/// Throw a string at runtime, or halt compilation with a `CTInternalFatalError`
[[noreturn]]//, gnu::noinline, gnu::cold]]
constexpr void throw_runtime ( std : : convertible_to < std : : string_view > auto & & msg ) {
std : : string_view view { msg } ;
if consteval {
using CTE = CTInternalFatalError ;
struct CTIFRuntimeError : public CTE {
# define CTE CTInternalFatalError
using S = decltype ( msg ) ;
struct CTIFRuntimeError final : public CTE {
using CTE : : CTE ;
consteval CTIFRuntimeError ( std: : string_view & & view ) noexcept
consteval CTIFRuntimeError ( S & & view ) noexcept
: CTE ( ) , m_message ( std : : move ( view ) ) { }
consteval std : : string_view message ( ) const noexcept override { return m_message ; }
consteval std : : string_view message ( ) const noexcept override { return { m_message } ; }
constexpr virtual ~ CTIFRuntimeError ( ) noexcept { }
private :
std: : string_view m_message ;
S m_message ;
} ;
throw CTIFRuntimeError ( std : : move ( view ) ) ;
# undef CTE
throw CTIFRuntimeError ( std : : move ( msg ) ) ;
} else {
throw_runtime (std : : move ( view) ) ;
static_cast < void ( std : : string_view & & ) > ( throw_runtime ) ({ std : : move ( msg) } ) ; //XXX: We may have to rename the above function (and make it [[internal]], or refactor to make it a private method of a function-object class `throw_runtime` to avoid ambiguations when constexpr-context calls it with the exact typed argument `std::string_view&&`... Maybe... I'm not really sure.
}
__builtin_unreachable ( ) ;
}
// Returns an anonymous union with inavtive field: `T value` and active field `assume init` (no unique address).
template < typename T > requires ( ! std : : is_reference_v < T > )
constexpr auto uninit ( ) noexcept {