# pragma once
# include <utility>
# include <functional>
# include <concepts>
# include <bit>
# include <stdexcept>
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__ ) ) & ;
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 ) . . . ) ;
}
struct NullException /*TODO : public error::Error*/ {
constexpr NullException ( ) noexcept = default ;
constexpr NullException ( const NullException & ) noexcept = default ;
constexpr NullException & operator = ( const NullException & ) noexcept = default ;
constexpr virtual ~ NullException ( ) noexcept { }
constexpr std : : string_view message ( ) const noexcept /*override*/ { return " pointer was null " ; }
std : : runtime_error as_runtime ( ) & & noexcept ; /*TODO: override*/
} ;
template < typename T > requires ( ! std : : is_reference_v < T > )
struct NonNull final {
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
}