# pragma once
# include <utility>
# include <functional>
# include <concepts>
# include <bit>
# include <stdexcept>
# include <tuple>
# include "util.hh"
# define _EO_DETAILS namespace details [[gnu::visibility("internal")]]
namespace exopt : : ptr {
template < typename T >
using RawPtr = T * ;
template < typename T >
using RawRef = T & ;
template < typename T >
using UniquePtr = T __restrict__ * ;
template < typename T >
using UniqueRef = T __restrict__ & ;
template < typename T >
using UniqueMoveRef = T __restrict__ & & ;
template < typename T >
using AliasedPtr = T __attribute__ ( ( __may_alias__ ) ) * ;
template < typename T >
using AliasedRef = T __attribute__ ( ( __may_alias__ ) ) & ;
using opaque_t = void ;
using address_t = uintptr_t ;
constexpr inline size_t address_bits_v = sizeof ( address_t ) * 8 ;
static_assert ( sizeof ( RawPtr < opaque_t > ) = = sizeof ( address_t ) , " Bad address_t width " ) ;
template < typename T , size_t Size >
concept IsSize = sizeof ( T ) = = Size ;
template < typename T >
concept PointerEqCmp = requires ( T p ) {
{ p = = nullptr } - > std : : convertible_to < bool > ;
} ;
template < typename T >
concept PointerMemCmp = PointerEqCmp < T > & & requires ( T p , T p2 ) {
{ p = = p2 } - > std : : convertible_to < bool > ;
{ static_cast < void * > ( p ) } ;
} ;
template < typename T >
concept PointerDeref = PointerEqCmp < T > & & requires ( T p ) {
{ * p } ;
} ;
template < typename T , typename To >
concept PointerDerefTo = PointerDeref < T > & & requires ( T p ) {
{ * p } - > std : : convertible_to < To > ;
} ;
template < PointerEqCmp T >
constexpr decltype ( auto ) map ( T & & ptr , auto & & func ) noexcept ( std : : is_nothrow_invocable_v < decltype ( func ) , decltype ( ptr ) > ) requires ( std : : is_invocable_v < decltype ( func ) , decltype ( ptr ) > )
{
if ( ptr = = nullptr ) return std : : forward < T > ( ptr ) ;
else return func ( std : : forward < T > ( ptr ) ) ;
}
template < PointerEqCmp T >
constexpr decltype ( auto ) map ( T & & ptr , auto & & func , auto & & other ) noexcept ( std : : is_nothrow_invocable_v < decltype ( func ) , decltype ( ptr ) > & & ( ! std : : is_invocable_v < decltype ( other ) > | | std : : is_nothrow_invocable_v < decltype ( other ) > ) ) requires ( std : : is_invocable_v < decltype ( func ) , decltype ( ptr ) > )
{
if ( ptr = = nullptr ) return std : : forward < T > ( ptr ) ;
else if constexpr ( std : : is_invocable_v < decltype ( other ) > ) return other ( ) ;
else return std : : forward < decltype ( other ) > ( other ) ;
}
template < PointerEqCmp T >
constexpr decltype ( auto ) not_null_or ( T & & ptr , auto & & other ) noexcept ( ! std : : is_invocable_v < decltype ( other ) > ) requires ( std : : is_convertible_v < decltype ( other ) , T > | | ( std : : is_invocable_v < decltype ( other ) > & & std : : is_convertible_v < std : : invoke_result_t < decltype ( other ) > , decltype ( ptr ) > ) )
{
if ( ! ( ptr = = nullptr ) ) return std : : forward < T > ( ptr ) ;
else if constexpr ( std : : is_invocable_v < decltype ( other ) > ) return other ( ) ;
else return std : : forward < decltype ( other ) > ( other ) ;
}
template < typename T , typename U >
constexpr bool addr_eq ( const T & o1 , const U & o2 ) noexcept { return static_cast < void * > ( std : : addressof ( o1 ) ) = = static_cast < void * > ( std : : addressof ( o2 ) ) ; }
template < typename U >
constexpr bool ptr_addr_eq ( const PointerMemCmp auto & ptr , const U & obj ) noexcept { return static_cast < void * > ( ptr ) = = static_cast < void * > ( std : : addressof ( obj ) ) ; }
template < PointerDeref U >
constexpr decltype ( auto ) deref_or ( U & & ptr , auto & & other ) noexcept ( noexcept ( * ptr ) & & ( ! std : : is_invocable_v < decltype ( other ) > | | std : : is_nothrow_invocable_v < decltype ( other ) > ) ) {
if ( ! ( ptr = = nullptr ) ) return * ptr ;
else if constexpr ( std : : is_invocable_v < decltype ( other ) > ) return other ( ) ;
else return std : : forward < decltype ( other ) > ( other ) ;
}
template < typename E , typename . . . Args > requires ( std : : is_constructible_v < E , Args . . . > )
constexpr decltype ( auto ) throw_if_null ( PointerEqCmp auto & & ptr , Args & & . . . error ) {
auto _throw = [ & ] [ [ noreturn , gnu : : noinline , gnu : : cold ] ] ( ) {
throw E { std : : forward < Args > ( error ) . . . } ;
} ;
if ( ptr = = nullptr ) _throw ( ) ;
return std : : forward < decltype ( ptr ) > ( ptr ) ;
}
template < typename E , typename . . . Args > requires ( std : : is_constructible_v < E , Args . . . > )
constexpr decltype ( auto ) deref_or_throw ( PointerDeref auto & & ptr , Args & & . . . error )
{
return * throw_if_null < E , Args . . . > ( std : : forward < decltype ( ptr ) > ( ptr ) , std : : forward < Args > ( error ) . . . ) ;
}
template < typename T >
constexpr auto read_once ( const T & ref ) noexcept ( std : : is_nothrow_copy_constructible_v < T > ) requires ( std : : is_copy_constructible_v < T > )
{
AliasedPtr < const T > aref = std : : addressof ( ref ) ;
return * aref ;
}
template < typename T >
constexpr decltype ( auto ) write_once ( T & ref , std : : convertible_to < T > auto & & value ) noexcept ( std : : is_nothrow_move_constructible_v < T > ) requires ( std : : is_move_constructible_v < decltype ( value ) > & & std : : is_constructible_v < T , decltype ( std : : move ( value ) ) > )
{
AliasedPtr < T > aref = std : : addressof ( ref ) ;
return * aref = std : : move ( value ) ;
}
template < typename T >
constexpr T read_once_strict ( const T & ref ) noexcept ( std : : is_nothrow_copy_constructible_v < T > ) requires ( std : : is_copy_constructible_v < T > )
{
return * static_cast < const volatile T * > ( std : : addressof ( ref ) ) ;
}
template < typename T >
constexpr void write_once_strict ( T & ref , std : : convertible_to < T > auto & & value ) noexcept ( std : : is_nothrow_move_constructible_v < T > ) requires ( std : : is_move_constructible_v < decltype ( value ) > & & std : : is_constructible_v < T , decltype ( std : : move ( value ) ) > )
{
* static_cast < volatile T * > ( std : : addressof ( ref ) ) = T { std : : move ( value ) } ;
}
template < typename T , typename . . . Args > requires ( std : : is_constructible_v < T , Args . . . > )
constexpr T & leak ( Args & & . . . ctor ) noexcept ( std : : is_nothrow_constructible_v < T , Args . . . > )
{
return * new T ( std : : forward < Args > ( ctor ) . . . ) ;
}
template < typename . . . Types >
struct NullDerefException : public NullDerefException < > {
using types = std : : tuple < Types . . . > ;
using NullDerefException < > : : NullDerefException ;
constexpr std : : string_view message ( ) const noexcept override {
/* constexpr auto value = util::concat_strings("pointer was null for types ", util::type_name<Types>()...);
return value ; */
return util : : concat_str_v < std : : string_view { " pointer was null for types " } , util : : type_name < Types > ( ) . . . > ;
}
constexpr virtual ~ NullDerefException ( ) noexcept { }
} ;
template < >
struct NullDerefException < > /*TODO : public error::Error*/ {
constexpr NullDerefException ( ) noexcept = default ;
constexpr NullDerefException ( const NullException & ) noexcept = default ;
constexpr NullDerefException & operator = ( const NullException & ) noexcept = default ;
constexpr virtual ~ NullDerefException ( ) noexcept { }
constexpr virtual std : : string_view message ( ) const noexcept /*override*/ { return " pointer was null " ; }
std : : runtime_error as_runtime ( ) & & noexcept ; /*TODO: override*/
} ;
using NullException = NullDerefException < > ;
template < typename T >
struct NullDerefException < T > : public NullDerefException < > {
using type = T ;
using NullDerefException < > : : NullDerefException ;
constexpr std : : string_view message ( ) const noexcept override {
return util : : concat_str_v < std : : string_view { " pointer was null for type " } , util : : type_name < type > ( ) > ;
}
constexpr virtual ~ NullDerefException ( ) noexcept { }
} ;
template < typename . . . Types >
consteval auto /*&&*/ null_exception ( ) noexcept {
/*NullException&& value = NullDerefException<Types...>{};
return std : : move ( value ) ; < - this isn ' t actually what we want I ' m pretty sure . . . */
return NullDerefException < Types . . . > { } ;
}
consteval NullException /*&&*/ null_exception ( ) noexcept { return { } ; }
template < typename T > requires ( ! std : : is_reference_v < T > )
struct NonNull {
using pointer_type = T * ;
using const_pointer_type = std : : remove_cv_t < T > const * ;
constexpr static inline bool is_mutable = ! std : : is_same_v < pointer_type , const_pointer_type > ;
constexpr NonNull ( T & ref ) noexcept
: ptr_ ( std : : addressof ( ref ) ) { }
[ [ gnu : : nonnull ( 1 , 2 ) ] ]
constexpr explicit NonNull ( T * ptr ) noexcept
: ptr_ ( ptr ) { }
constexpr NonNull ( const NonNull & ) noexcept = default ;
constexpr NonNull ( NonNull & & ) noexcept = default ;
constexpr NonNull & operator = ( const NonNull & ) noexcept = default ;
constexpr NonNull & operator = ( NonNull & & ) noexcept = default ;
constexpr ~ NonNull ( ) noexcept = default ;
constexpr static NonNull < T > try_new ( T * ptr ) {
if ( __builtin_expect ( ! ptr , false ) ) _throw_null ( ) ;
return NonNull < T > { ptr } ;
}
# ifdef DEBUG
[ [ gnu : : nonnull ( 1 ) ] ]
# endif
constexpr static NonNull < T > new_unchecked ( T * ptr ) noexcept {
# ifndef DEBUG
_EO_ASSUME ( ptr ! = nullptr ) ;
# endif
return NonNull { ptr } ; }
constexpr friend bool operator = = ( std : : nullptr_t , const NonNull & ) noexcept { return false ; }
constexpr friend bool operator = = ( const NonNull & , std : : nullptr_t ) noexcept { return false ; }
constexpr friend bool operator ! = ( std : : nullptr_t , const NonNull & ) noexcept { return true ; }
constexpr friend bool operator ! = ( const NonNull & , std : : nullptr_t ) noexcept { return true ; }
constexpr friend auto operator < = > ( const NonNull & a , const NonNull & b ) noexcept { return a . get ( ) < = > b . get ( ) ; }
constexpr friend auto operator < = > ( const NonNull & a , T * b ) noexcept { return ( ! b ) ? ( true < = > false ) : ( a . get ( ) < = > b ) ; }
constexpr friend auto operator < = > ( T * a , const NonNull & b ) noexcept { return ( ! a ) ? ( false < = > true ) : ( a < = > b . get ( ) ) ; }
template < typename U >
constexpr NonNull < U > cast ( ) const noexcept requires ( requires ( T * ptr ) { static_cast < U * > ( ptr ) ; } )
{
return NonNull < U > : : new_unchecked ( static_cast < U * > ( get ( ) ) ) ;
}
template < typename U >
constexpr NonNull < U > try_cast ( ) const requires ( requires ( T * ptr ) { dynamic_cast < U * > ( ptr ) ; } )
{
return NonNull < U > : : try_new ( dynamic_cast < U * > ( get ( ) ) ) ;
}
template < typename Func > requires ( std : : is_invocable_v < Func , T & > )
constexpr auto map_value ( Func const & mapper ) & noexcept ( std : : is_nothrow_invocable_v < Func , T & > )
- > NonNull < std : : add_pointer_t < std : : invoke_result_t < Func , T & > > > //TODO: Can we extend this for: void returns, pointer (not reference) returns, maybe even std::convertible_to<?*> returns?
{ return { std : : addressof ( std : : invoke ( std : : forward < decltype ( mapper ) > ( mapper ) , static_cast < T & > ( * get ( ) ) ) ) } ; }
template < typename Func > requires ( std : : is_invocable_v < Func , T const & > )
constexpr auto map_value ( Func const & mapper ) const & noexcept ( std : : is_nothrow_invocable_v < Func , T const & > )
- > NonNull < std : : add_pointer_t < std : : invoke_result_t < Func , T const & > > > //TODO: Can we extend this for: void returns, pointer (not reference) returns, maybe even std::convertible_to<?*> returns?
{ return { std : : addressof ( std : : invoke ( std : : forward < decltype ( mapper ) > ( mapper ) , static_cast < T const & > ( * get ( ) ) ) ) } ; }
template < typename Func > requires ( std : : is_invocable_v < Func , T & & > )
constexpr auto map_value ( Func & & mapper ) & & noexcept ( std : : is_nothrow_invocable_v < Func , T & & > )
- > NonNull < std : : add_pointer_t < std : : invoke_result_t < Func , T & & > > > //TODO: Can we extend this for: void returns, pointer (not reference) returns, maybe even std::convertible_to<?*> returns?
{ return { std : : addressof ( std : : invoke ( std : : forward < decltype ( mapper ) > ( mapper ) , std : : move ( * get ( ) ) ) ) } ; }
template < typename Func > requires ( std : : is_invocable_v < Func , T const & & > )
constexpr auto map_value ( Func & & mapper ) const & & noexcept ( std : : is_nothrow_invocable_v < Func , T const & & > )
- > NonNull < std : : add_pointer_t < std : : invoke_result_t < Func , T const & & > > > //TODO: Can we extend this for: void returns, pointer (not reference) returns, maybe even std::convertible_to<?*> returns?
{ return { std : : addressof ( std : : invoke ( std : : forward < decltype ( mapper ) > ( mapper ) , std : : move ( static_cast < T const & > ( * get ( ) ) ) ) ) } ; }
template < typename U >
constexpr explicit operator NonNull < U > ( ) const noexcept requires ( requires ( T * ptr ) { static_cast < U * > ( ptr ) ; } )
{
return cast < U > ( ) ;
}
[ [ gnu : : returns_nonnull ] ]
constexpr T * get ( ) const noexcept { return ptr_ ; }
[ [ gnu : : returns_nonnull ] ]
constexpr operator T * ( ) const noexcept { return ptr_ ; }
constexpr T & operator * ( ) & noexcept { return * ptr_ ; }
constexpr const T & operator * ( ) const & noexcept { return * ptr_ ; }
constexpr T & & operator * ( ) & & noexcept { return std : : move ( * ptr_ ) ; }
constexpr decltype ( auto ) operator * ( ) const & & noexcept { return * ptr_ ; }
[ [ gnu : : returns_nonnull ] ]
constexpr T * operator - > ( ) noexcept { return ptr_ ; }
[ [ gnu : : returns_nonnull ] ]
constexpr const T * operator - > ( ) const noexcept { return ptr_ ; }
private :
[ [ noreturn , gnu : : noinline , gnu : : cold ] ]
constexpr static void _throw_null ( ) {
if consteval {
throw NullException { } ;
} else {
//std::terminate();
throw NullException { } . as_runtime ( ) ;
//::exopt::util::throw_runtime(NullException{}); //XXX: Wut?
}
//throw error::as_runtime_error(error::comptime_fail<NullException>());
}
T * ptr_ ;
} ;
/// Holds a *unique reference* that cannot be aliased, and cannot be null.
template < typename T > requires ( ! std : : is_reference_v < T > )
struct Unique {
using value_type = std : : remove_cv_t < std : : decay_t < T > > ;
using reference_type = UniqueRef < value_type > ;
using const_reference_type = UniqueRef < value_type const > ;
using move_reference_type = UniqueMoveRef < value_type > ;
using pointer_type = UniquePtr < value_type > ;
using const_pointer_type = UniquePtr < value_type const > ;
constexpr static inline bool is_mutable = ! std : : is_same_v < pointer_type , const_pointer_type > ;
constexpr static Unique < T > try_new ( pointer_type const & & ptr )
{
return { NonNull < T > : : try_new ( ptr ) } ;
}
# ifdef DEBUG
[ [ gnu : : nonnull ( 1 ) ] ]
# endif
constexpr static Unique < T > new_unchecked ( pointer_type ptr ) noexcept {
# ifndef DEBUG
_EO_ASSUME ( ptr ! = nullptr ) ;
# endif
return { NonNull < T > { ptr } } ; }
constexpr Unique ( std : : convertible_to < NonNull < value_type > > auto const & & ptr ) noexcept
: m_ptr ( ptr ) { }
[ [ gnu : : nonnull ( 1 , 2 ) ] ]
constexpr explicit Unique ( T * __restrict__ ptr ) noexcept
: m_ptr ( ptr ) { }
constexpr Unique ( Unique & & ) noexcept = default ;
constexpr Unique & operator = ( Unique & & ) noexcept = default ;
constexpr Unique ( const Unique & ) = delete ;
constexpr Unique & operator = ( const Unique & ) = delete ;
constexpr ~ Unique ( ) noexcept = default ;
constexpr friend bool operator = = ( std : : nullptr_t , const Unique & __restrict__ ) noexcept { return false ; }
constexpr friend bool operator = = ( const Unique & __restrict__ , std : : nullptr_t ) noexcept { return false ; }
constexpr friend bool operator ! = ( std : : nullptr_t , const Unique & __restrict__ ) noexcept { return true ; }
constexpr friend bool operator ! = ( const Unique & __restrict__ , std : : nullptr_t ) noexcept { return true ; }
constexpr friend auto operator < = > ( const Unique & __restrict__ a , const Unique & __restrict__ b ) noexcept { return a . get ( ) < = > b . get ( ) ; }
constexpr friend auto operator < = > ( const Unique & __restrict__ a , T * __restrict__ b ) noexcept { return ( ! b ) ? ( true < = > false ) : ( a . get ( ) < = > b ) ; }
constexpr friend auto operator < = > ( T * __restrict__ a , const Unique & __restrict__ b ) noexcept { return ( ! a ) ? ( false < = > true ) : ( a < = > b . get ( ) ) ; }
[ [ gnu : : returns_nonnull ] ]
constexpr pointer_type get ( ) const __restrict__ noexcept { return m_ptr . get ( ) ; }
[ [ gnu : : returns_nonnull ] ]
constexpr operator pointer_type ( ) const __restrict__ noexcept { return get ( ) ; }
constexpr explicit operator NonNull < T > ( ) const noexcept { return m_ptr ; }
constexpr reference_type operator * ( ) __restrict__ & noexcept { return * get ( ) ; }
constexpr const_reference_type operator * ( ) const __restrict__ & noexcept { return * get ( ) ; }
constexpr move_reference_type operator * ( ) __restrict__ & & noexcept { return std : : move ( * get ( ) ) ; }
constexpr decltype ( auto ) operator * ( ) const __restrict__ & & noexcept { return * get ( ) ; }
[ [ gnu : : returns_nonnull ] ]
constexpr pointer_type operator - > ( ) __restrict__ noexcept { return get ( ) ; }
[ [ gnu : : returns_nonnull ] ]
constexpr const_pointer_type operator - > ( ) const __restrict__ noexcept { return get ( ) ; }
private :
NonNull < T > m_ptr ;
} ;
#if 0
consteval bool uniq_check ( ) noexcept {
int ptr = 0 ;
int * const ptr0 = & ptr ;
return Unique < int > : : try_new ( std : : move ( ptr0 ) ) . get ( ) = = & ptr ;
}
static_assert ( uniq_check ( ) , " Invalid Unique<>.get() " ) ;
# endif
_EO_DETAILS {
[ [ gnu : : const ] ]
address_t hash ( const opaque_t * ) noexcept ;
}
/// Hash a pointer and retrieve a scrambled, unique value representing that memory address.
[ [ gnu : : const ] ]
constexpr address_t hash ( const opaque_t * ptr ) noexcept {
if consteval {
throw " TODO: How to hash pointer in constexpr context? " ;
} else {
return details : : hash ( ptr ) ;
}
}
}
# undef _EO_DETAILS