# pragma once
# include <memory>
# include <string_view>
# include <utility>
# include <array>
# 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 < typename T , typename U >
concept comptime = std : : convertible_to < T , U > and requires {
typename T : : comptime_constant_t ;
} ;
template < typename T >
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 < U > m ) noexcept : value ( std : : move ( m ) ) { }
constexpr ~ inner ( ) = default ;
} ;
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 , 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
{
constexpr decltype ( auto ) _array_create = [ ] < size_t . . . I > ( auto const & a , std : : index_sequence < I . . . > ) noexcept
{
return Array { a [ I ] . . . } ;
} ;
return _array_create ( std : : move ( a ) , _I { } ) ;
}
template < typename T , size_t N , typename Array = std : : array < T , N > , typename _I = std : : make_index_sequence < N > >
constexpr auto array_literal ( T ( & & a ) [ N ] ) noexcept
- > Array
{
constexpr decltype ( auto ) _array_create = [ ] < size_t . . . I > ( auto & & a , std : : index_sequence < I . . . > ) noexcept
{
return Array { std : : move ( a [ I ] ) . . . } ;
} ;
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 < typename R , typename . . . Args >
constexpr auto map ( auto const & fun , Args & & . . . values ) noexcept ( ( std : : is_nothrow_invocable_v < decltype ( fun ) , Args > & & . . . ) )
requires ( ( std : : is_invocable_v < decltype ( fun ) , Args > & & . . . ) and (
( 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 < ( std : : is_void_v < std : : invoke_result_t < decltype ( fun ) , Args > > | | . . . )
, void
, R >
{
if constexpr ( ( std : : is_void_v < std : : invoke_result_t < decltype ( fun ) , Args > > | | . . . ) ) {
( ( 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 < 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 . . . > ;
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 ;
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 ;
} ;
/// 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 ) {
if consteval {
# define CTE CTInternalFatalError
using S = decltype ( msg ) ;
struct CTIFRuntimeError final : public CTE {
using CTE : : CTE ;
consteval CTIFRuntimeError ( S & & view ) noexcept
: CTE ( ) , m_message ( std : : move ( view ) ) { }
consteval std : : string_view message ( ) const noexcept override { return { m_message } ; }
constexpr virtual ~ CTIFRuntimeError ( ) noexcept { }
private :
S m_message ;
} ;
# undef CTE
throw CTIFRuntimeError ( std : : move ( msg ) ) ;
} else {
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 {
union UI {
struct assume { } ;
T value ;
[[no_unique_address]] assume init { } ;
} ;
return UI { } ;
}
template < typename T > requires ( ! std : : is_reference_v < T > )
struct alignas ( T ) maybe_uninit {
using uninit = std : : array < unsigned char , sizeof ( T ) > ;
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 < T > )
{ m_init . ~ T ( ) ; m_uninit = { } ; }
[[nodiscard]]
constexpr T assume_init_take ( ) noexcept ( std : : is_nothrow_move_constructible_v < T > )
{
auto v = std : : move ( m_init ) ;
assume_init_drop ( ) ;
return v ;
}
constexpr maybe_uninit < T > init ( T & & value ) noexcept ( std : : is_nothrow_move_constructible_v < T > )
{ return maybe_uninit < T > { std : : move ( value ) , EXPLICIT_INIT_TAG < EXPLICIT_INIT_MOVE > { } } ; }
template < typename . . . Args > requires ( std : : is_constructible_v < T , Args . . . > )
constexpr maybe_uninit < T > init_in_place ( Args & & . . . args ) noexcept ( std : : is_nothrow_constructible_v < T , Args . . . > )
{ return maybe_uninit < T > { EXPLICIT_INIT_TAG < EXPLICIT_INIT_IN_PLACE > { } , std : : forward < Args > ( args ) . . . } ; }
private :
enum EXPLICIT_INIT_TYPE {
EXPLICIT_INIT_IN_PLACE ,
EXPLICIT_INIT_MOVE ,
} ;
template < EXPLICIT_INIT_TYPE >
struct EXPLICIT_INIT_TAG { } ;
constexpr maybe_uninit ( T & & value , EXPLICIT_INIT_TAG < EXPLICIT_INIT_MOVE > ) noexcept ( std : : is_nothrow_move_constructible_v < T > )
: m_init { std : : move ( value ) } { }
template < typename . . . Args > requires ( std : : is_constructible_v < T , Args . . . > )
constexpr maybe_uninit ( EXPLICIT_INIT_TAG < EXPLICIT_INIT_IN_PLACE > _a , Args & & . . . args ) noexcept ( std : : is_nothrow_constructible_v < T , Args . . . > )
: m_init { std : : forward < Args > ( 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 < std : : string_view > ( ) ;
maybe_uninit < std : : string_view > mi ;
ui . init . ~ assume ( ) ;
ui . value = std : : string_view { " Hello " } ;
return std : : move ( ui . value ) ;
}
static_assert ( ui_test ( ) [ 0 ] = = ' H ' , " Bad uninit<T>() " ) ;
}
} }