# pragma once
# include <optional>
# include <utility>
# include <concepts>
# include "pointer.h"
# include "exopt.h"
/// A better std::optional
namespace exopt : : types { namespace optional [ [ gnu : : visibility ( " internal " ) ] ] {
template < typename T >
struct null_optimise ;
template < typename T >
struct null_optimise < ptr : : NonNull < T > > { constexpr static inline bool value = true ;
using held_type = T * ;
using type = ptr : : NonNull < T > ;
constexpr static decltype ( auto ) convert_to_held ( std : : add_rvalue_reference_t < type > t ) noexcept { return t . get ( ) ; }
constexpr static decltype ( auto ) convert_from_held ( std : : add_rvalue_reference_t < held_type > t ) noexcept { return ptr : : NonNull < T > : : new_unchecked ( t ) ; }
constexpr static inline auto sentinel = nullptr ;
} ;
template < typename T >
struct null_optimise < std : : reference_wrapper < T > > { constexpr static inline bool value = true ;
using held_type = T * ;
using type = std : : reference_wrapper < T > ;
constexpr static decltype ( auto ) convert_to_held ( std : : add_rvalue_reference_t < type > t ) noexcept { return std : : addressof ( t . get ( ) ) ; }
constexpr static decltype ( auto ) convert_from_held ( std : : add_rvalue_reference_t < held_type > t ) noexcept { return std : : ref < T > ( * t ) ; }
constexpr static inline auto sentinel = nullptr ;
} ;
template < typename T >
class null_optimise < boxed : : Box < T > > {
using boxed : : Box ;
using box_t = Box < T > ;
class /*XXX: Shouldn't be needed alignas(box_t)*/ invariant : box_t {
//TODO: See phone notes.
using box_t : : UNSAFE ;
public :
constexpr box_t & operator * ( ) & noexcept { return * static_assert < box_t * > ( this ) ; }
constexpr box_t const & operator * ( ) const & noexcept { return * static_assert < box_t const * > ( this ) ; }
constexpr box_t & & operator * ( ) & & noexcept { return std : : move ( * static_assert < box_t * > ( this ) ) ; }
constexpr box_t const & & operator * ( ) const & & noexcept { return std : : move ( * static_assert < box_t const * > ( this ) ) ; }
constexpr static invariant & & back_conv ( box_t & & b ) noexcept { return static_cast < invariant & & > ( b ) ; }
constexpr static invariant const & back_conv ( box_t const & b ) noexcept { return static_cast < invariant const & > ( b ) ; }
constexpr static invariant & back_conv ( box_t & b ) noexcept { return static_cast < invariant & > ( b ) ; }
constexpr static invariant const & & back_conv ( box_t const & & b ) noexcept { return static_cast < invariant const & & > ( b ) ; }
constexpr invariant ( box_t & & m ) noexcept
: box_t ( UNSAFE , std : : move ( std : : move ( m ) . as_unique ( UNSAFE ) ) ) { }
constexpr invariant ( box_t const & m ) noexcept
: box_t ( UNSAFE , m ) { }
constexpr invariant ( std : : unique_ptr < T > & & m ) noexcept
: box_t ( UNSAFE , std : : move ( m ) ) { }
constexpr invariant ( std : : nullptr_t ) noexcept
: box_t ( UNSAFE , std : : unique_ptr < T > { nullptr } ) { }
constexpr friend auto operator < = > ( invariant const & a , invariant const & b ) noexcept { return ( * a ) . as_unsafe_ptr ( UNSAFE ) < = > ( * b ) . as_unsafe_ptr ( UNSAFE ) ; }
constexpr friend auto operator < = > ( invariant const & a , box_t const & b ) noexcept { return ( * a ) . as_unsafe_ptr ( UNSAFE ) < = > b . as_unsafe_ptr ( UNSAFE ) ; }
constexpr friend auto operator < = > ( invariant const & a , T const * p ) noexcept { return ( * a ) . as_unsafe_ptr ( UNSAFE ) < = > p ; }
constexpr friend auto operator < = > ( invariant const & a , std : : nullptr_t ) noexcept { return ( * a ) . as_unsafe_ptr ( UNSAFE ) < = > nullptr ; }
constexpr invariant & operator = ( box_t & & ) noexcept = delete ;
constexpr invariant & operator = ( box_t const & ) = delete ;
constexpr invariant & operator = ( invariant & & ) noexcept = default ;
constexpr invariant & operator = ( invariant const & ) = default ;
constexpr invariant & operator = ( std : : nullptr_t )
{ box_t : : as_unique ( UNSAFE ) . reset ( ) ; return * this ; }
constexpr invariant & operator = ( T * const & & p )
{ box_t : : as_unique ( UNSAFE ) . reset ( p ) ; return * this ; }
constexpr invariant ( invariant & & m ) noexcept
: invariant ( std : : move ( ( * std : : move ( m ) ) . as_unique ( UNSAFE ) ) ) { }
constexpr invariant ( invariant const & m ) noexcept
: box_t ( UNSAFE , ( * m ) . as_unique ( UNSAFE ) ) { }
constexpr ~ invariant ( ) = default ;
} ;
static_assert ( util : : shares_layout < invariant , box_t > , " invariant (held_type) does not share layout with viewed type (Box<T>) " ) ;
public :
typedef invariant held_type ; // invariant ^: Held internal type.
using type = box_t ; // Box<T>: API seen & operated on type
constexpr static decltype ( auto ) convert_to_held ( type ty ) noexcept { return held_type { std : : move ( ty ) } ; }
constexpr static decltype ( auto ) convert_to_held ( type & ty ) noexcept { return held_type : : back_conv ( ty ) ; }
constexpr static decltype ( auto ) convert_to_held ( type & & ty ) noexcept { return held_type : : back_conv ( std : : move ( ty ) ) ; }
constexpr static decltype ( auto ) convert_to_held ( type const & ty ) noexcept { return held_type : : back_conv ( ty ) ; }
constexpr static decltype ( auto ) convert_to_held ( type const & & ty ) noexcept { return held_type : : back_conv ( std : : move ( ty ) ) ; }
constexpr static type & convert_from_held ( held_type & ty ) noexcept { return * ty ; }
constexpr static type const & & convert_from_held ( held_type const & & ty ) noexcept { return std : : move ( * std : : move ( ty ) ) ; }
constexpr static type & & convert_from_held ( held_type & & ty ) noexcept { return std : : move ( * std : : move ( ty ) ) ; }
constexpr static type const & convert_from_held ( held_type const & ty ) noexcept { return * ty ; }
//TODO: See phone notes.
constexpr static inline auto sentinel = nullptr ;
} ;
# ifdef DEBUG / * We don't want to instantiate `null_optimise<>` *at all* if we don't have to outside of actual real use in `Option<T>`. This check is for development only since Option<Box<T>> is a bit of an awkward developmental case.
Save compile time for release builds , the ` static_assert ` inside the null - opt partial spec for ` . . : : invariant ` ' s layout will be checked if / when ` Option < Box < T > > ` is ever instantiated . This check is the same just assuring it ' s valid for * if * Option < Box < T > > is ever instantiated */
static_assert ( ! requires ( requires { typename boxed : : Box < int > ; } ) or // Skip check if `boxed.h` is not included.
requires ( requires ( null_optimise < boxed : : Box < int > > : : held_type const & held ) {
{ static_cast < boxed : : Box < int > const & > ( held ) } ;
} ) , " Nullopt held_type for Box<T> either has a unneeded and unwanted generated vtable or has invalid alignment, check into that " ) ;
# endif
template < typename T >
struct null_optimise < ptr : : Unique < T > > { constexpr static inline bool value = true ;
using held_type = T __restrict__ * ;
using type = ptr : : Unique < T > ;
constexpr static decltype ( auto ) convert_to_held ( std : : add_rvalue_reference_t < type > t ) noexcept { return t . get ( ) ; }
constexpr static decltype ( auto ) convert_from_held ( std : : add_rvalue_reference_t < held_type > t ) noexcept { return ptr : : Unique < T > : : new_unchecked ( t ) ; }
constexpr static inline auto sentinel = nullptr ;
} ;
template < typename T >
struct null_optimise < T & > { constexpr static inline bool value = true ;
using held_type = T * ;
using type = T & ;
constexpr static decltype ( auto ) convert_to_held ( type t ) noexcept { return std : : addressof ( t ) ; }
constexpr static auto & convert_from_held ( held_type h ) noexcept { return static_cast < type > ( * h ) ; }
constexpr static inline auto sentinel = nullptr ;
} ;
/* XXX: We can't optimise for this case with this model...
template < typename T > requires ( sizeof ( T & & ) < = sizeof ( T * ) )
struct null_optimise < T & & > { constexpr static inline bool value = true ;
using held_type = T ;
using type = T & & ;
constexpr static decltype ( auto ) convert_to_held ( type t ) noexcept ( std : : is_nothrow_move_constructible_v < T > ) { return std : : move ( t ) ; }
constexpr static auto & & convert_from_held ( held_type h ) noexcept { return std : : forward < type > ( h ) ; }
constexpr static inline auto sentinel = nullptr ;
} ; */
template < typename T >
struct null_optimise < T & & > { constexpr static inline bool value = true ; //sizeof(T&&) == sizeof(T*);
using held_type = T * ;
using type = T & & ;
constexpr static decltype ( auto ) convert_to_held ( type t ) noexcept { return std : : addressof ( t ) ; }
constexpr static auto & & convert_from_held ( held_type h ) noexcept { return std : : forward < type > ( * h ) ; }
constexpr static inline auto sentinel = nullptr ;
} ;
template < >
struct null_optimise < void > { constexpr static inline bool value = true ;
using held_type = void ;
using type = void ;
} ;
template < typename T >
concept has_null_opt = std : : is_void_v < T >
| | requires {
typename null_optimise < T > : : type ;
typename null_optimise < T > : : held_type ;
requires ( null_optimise < T > : : value ) ;
/*requires(std::is_void_v<null_optimise<T>::held_type> || requires(std::add_rvalue_reference_t< null_optimise<T>::held_type> held) {
{ null_optimise < T > : : sentinel = = std : : as_const ( held ) } - > std : : convertible_to < bool > ;
} ) ; */
/*requires(null_optimise<T>::type unheld
, std : : add_rvalue_reference_t < null_optimise < T > : : held_type > held ) {
//, const std::remove_reference_t<null_optimise<T>::held_type>& held_const) {
{ null_optimise < T > : : sentinel } - > std : : convertible_to < null_optmimise < T > : : held_type > ;
{ null_optimise < T > : : convert_to_held ( unheld ) } - > std : : convertible_to < null_optimise < T > : : held_type > ;
{ null_optimise < T > : : convert_from_held ( held ) } - > std : : convertible_to < null_optimise < T > : : type > ;
{ null_optimise < T > : : sentinel = = std : : as_const ( held ) } - > std : : convertible_to < bool > ;
//{ null_optimise<T>::convert_from_held(held_const) } -> std::convertible_to<null_optimise<T>::type>;
} ;
requires ( std : : is_same_v < T , null_optimise < T > : : type > ) ; */
} ;
namespace _null_opt_impl [ [ gnu : : visibility ( " hidden " ) ] ] {
template < typename , bool > struct types_for ;
template < typename T >
struct types_for < T , true > {
static_assert ( has_null_opt < T > , " Type does not have nullptr optimisation enabled " ) ;
using held = null_optimise < T > : : held_type ;
using type = null_optimise < T > : : type ;
} ;
template < typename T >
struct types_for < T , false > {
using held = T ;
using type = T ;
} ;
template < typename T >
using held_type = typename types_for < T , has_null_opt < T > > : : held ;
template < typename T >
using used_type = typename types_for < T , has_null_opt < T > > : : type ;
}
namespace _details [ [ gnu : : visibility ( " hidden " ) ] ] {
template < typename T >
struct not_void { using type = T ; } ;
template < >
struct not_void < void > { struct type ; } ;
template < typename T >
using not_void_t = typename not_void < T > : : type ;
template < typename T >
struct rv_ref_or_void { using type = std : : add_rvalue_reference_t < T > ; } ;
template < >
struct rv_ref_or_void < void > { struct type ; } ;
template < typename T >
using rv_ref_t = typename rv_ref_or_void < T > : : type ;
template < typename T >
struct cv_ref_or_void { using type = const std : : remove_reference_t < T > & ; } ;
template < >
struct cv_ref_or_void < void > { struct _type ; using type = const _type & ; } ;
template < typename T >
using cv_ref_t = typename cv_ref_or_void < T > : : type ;
template < typename T >
struct const_ref_or_void { using type = const std : : remove_const_t < T > & ; } ;
template < >
struct const_ref_or_void < void > { struct type ; } ;
template < typename T >
using const_ref_t = typename const_ref_or_void < T > : : type ;
template < typename T , typename U , bool >
struct if_then_else_type ;
template < typename If , typename U >
struct if_then_else_type < If , U , true > { using type = If ; using other = U ; constexpr static inline bool result = true ; } ;
template < typename T , typename Else >
struct if_then_else_type < T , Else , false > { using type = Else ; using other = T ; constexpr static inline bool result = false ; } ;
template < bool Predicate , typename Then , typename Else >
using if_then_else_t = typename if_then_else_type < Then , Else , Predicate > : : type ;
template < typename T >
class any_ref_or_void {
template < typename _U = T , bool P = std : : is_rvalue_reference_v < T > >
struct is_rv_ref_type ;
template < typename U >
struct is_rv_ref_type < U , true > { using type = U ; static_assert ( std : : is_rvalue_reference_v < type > , " Invalid reference " ) ; } ;
template < typename U >
struct is_rv_ref_type < U , false > { using type = U & ; static_assert ( ! std : : is_rvalue_reference_v < U > , " Invalid reference " ) ; } ;
public :
using type = is_rv_ref_type < T , std : : is_rvalue_reference_v < T > > : : type ;
} ;
/* XXX: Having to account for all qualifiers is fucking dreck... we'd also need all variations of this with `__restrict__` too..
template < typename T >
struct any_ref_or_void < T & & > { using type = T & & ; } ;
template < typename T >
struct any_ref_or_void < const T & & > { using type = const T & & ; } ;
template < typename T >
struct any_ref_or_void < volatile T & & > { using type = volatile T & & ; } ;
template < typename T >
struct any_ref_or_void < const volatile T & & > { using type = const volatile T & & ; } ;
*/
template < >
struct any_ref_or_void < void > { struct type ; } ;
template < typename T >
using any_ref_t = typename any_ref_or_void < T > : : type ;
template < typename T >
struct mut_ref_or_void { using type = any_ref_or_void < std : : remove_const_t < T > > : : type ; } ;
template < >
struct mut_ref_or_void < void > { struct type ; } ;
template < typename T >
using mut_ref_t = typename mut_ref_or_void < T > : : type ;
template < typename T > requires ( requires ( T v ) { { v = = nullptr } - > std : : convertible_to < bool > ; } )
[ [ gnu : : always_inline ] ]
constexpr decltype ( auto ) map_null ( T & & value , auto & & with ) noexcept ( std : : is_nothrow_invocable_v < decltype ( with ) , decltype ( std : : forward < T > ( value ) ) > ) { return ( ! bool ( value = = nullptr ) ) ? with ( std : : forward < T > ( value ) ) : nullptr ; }
template < typename T , typename U > requires ( requires ( T v ) { { v = = nullptr } - > std : : convertible_to < bool > ; } )
[ [ gnu : : always_inline ] ]
constexpr decltype ( auto ) map_null ( T & & value , auto & & with , std : : add_rvalue_reference_t < U > otherwise ) noexcept ( std : : is_nothrow_invocable_v < decltype ( with ) , decltype ( std : : forward < T > ( value ) ) > & & ( ! std : : is_invocable_v < U > | | std : : is_nothrow_invocable_v < decltype ( otherwise ) > ) ) {
if ( ! bool ( value = = nullptr ) ) return with ( std : : forward < T > ( value ) ) ;
else if constexpr ( std : : is_invocable_v < U > ) return otherwise ( ) ;
else return std : : forward < decltype ( otherwise ) > ( otherwise ) ;
}
template < typename T , typename . . . Types >
constexpr inline bool is_same_any_v = ( std : : is_same_v < T , Types > | | . . . ) ;
constexpr inline auto identity = [ ] < typename T > ( T v ) noexcept { return std : : forward < T > ( v ) ; } ;
}
struct none_t {
constexpr none_t ( ) noexcept = default ;
constexpr ~ none_t ( ) noexcept = default ;
constexpr none_t ( std : : nullptr_t ) noexcept : none_t ( ) { }
constexpr none_t ( std : : nullopt_t ) noexcept : none_t ( ) { }
constexpr operator std : : nullptr_t ( ) const noexcept { return nullptr ; }
} ;
constexpr inline none_t None ;
template < typename T >
concept AsOption = ( std : : is_void_v < T >
| | requires { requires ( sizeof ( T ) = = sizeof ( T ) ) ; }
)
& & ! _details : : is_same_any_v < T , none_t , std : : nullptr_t > ;
struct OptionUnwrapError /*TODO : public virtual error::Error, virtual std::exception*/
{
constexpr static inline const auto MESSAGE = " Unwrap operation on a None value " ;
OptionUnwrapError ( ) noexcept = default ;
inline virtual ~ OptionUnwrapError ( ) noexcept { }
inline std : : string_view message ( ) const noexcept /*override*/ { return std : : string_view { MESSAGE } ; }
} ;
template < AsOption T >
struct Option final {
struct UnwrapError : public OptionUnwrapError
{
UnwrapError ( ) noexcept = default ;
inline UnwrapError ( const UnwrapError & ) noexcept : UnwrapError ( ) { }
inline UnwrapError ( UnwrapError & & ) noexcept : UnwrapError ( ) { }
inline virtual ~ UnwrapError ( ) noexcept { }
using OptionUnwrapError : : message ;
} ;
constexpr static inline bool is_null_optimised = has_null_opt < T > ;
constexpr friend void swap ( Option & a , Option & b ) noexcept {
using std : : swap ;
swap ( a . inner_ , b . inner_ ) ; //XXX: Swap the values if possible?
}
constexpr Option ( std : : optional < T > & & opt ) noexcept requires ( ! std : : is_void_v < T > )
: inner_ ( bool ( opt ) ? std : : move ( * opt ) : nullptr ) { }
constexpr explicit Option ( std : : nullptr_t ) noexcept : inner_ ( nullptr ) { }
constexpr Option ( none_t ) noexcept : Option ( nullptr ) { }
constexpr ~ Option ( ) noexcept ( std : : is_nothrow_destructible_v < T > ) { }
constexpr Option ( const Option & copy ) noexcept ( std : : is_nothrow_copy_constructible_v < decltype ( copy . inner_ ) > )
: inner_ ( copy . inner_ )
{ }
constexpr Option ( Option & & move ) noexcept ( std : : is_nothrow_move_constructible_v < decltype ( move . inner_ ) > )
: inner_ ( std : : move ( move . inner_ ) )
{ }
constexpr explicit ( std : : convertible_to < std : : add_rvalue_reference_t < T > , Option < T > > ) Option ( _details : : rv_ref_t < T > value ) noexcept ( std : : is_nothrow_invocable_v < convert_to_held_rv , std : : add_rvalue_reference_t < T > > ) requires ( ! std : : is_void_v < T > )
: inner_ ( convert_to_held_rv ( std : : forward < decltype ( value ) > ( value ) ) )
{ }
//TODO: copy/move assign where appropriate
constexpr Option & operator = ( const Option & copy ) noexcept ( std : : is_void_v < T > | | std : : is_nothrow_copy_constructible_v < decltype ( copy . inner_ ) : : held_type > )
{
if constexpr ( std : : is_void_v < T > ) {
inner_ . has = copy . inner_ . has ;
} else {
if ( this ! = std : : addressof ( copy ) ) {
using inner_t = std : : remove_reference_t < decltype ( inner_ ) > ;
if ( const auto * ptr = copy . try_get_value ( ) ) {
using held_t = inner_t : : held_type ;
inner_ = inner_t { held_t { * ptr } } ;
} else inner_ = inner_t { None } ;
}
} return * this ;
}
constexpr Option & operator = ( Option & & move ) noexcept ( std : : is_void_v < T > | | std : : is_nothrow_move_constructible_v < decltype ( move . inner_ ) : : held_type > )
{
if constexpr ( std : : is_void_v < T > ) std : : exchange ( inner_ . has , move . inner_ . has ) ;
else {
if ( this ! = std : : addressof ( move ) ) {
using inner_t = std : : remove_reference_t < decltype ( inner_ ) > ;
if ( auto * ptr = move . try_get_value ( ) ) {
inner_ = inner_t { std : : move ( * ptr ) } ;
} else inner_ = inner_t { None } ;
}
} return * this ;
}
constexpr Option & operator = ( none_t ) noexcept ( std : : is_nothrow_destructible_v < decltype ( std : : declval < Option < T > > ( ) . inner_ ) : : held_type > )
{
using inner_t = std : : remove_reference_t < decltype ( inner_ ) > ;
inner_ = inner_t { None } ;
return * this ;
}
//TODO: XXX: For this to work, we need *another* partial-spec "trait" that is (T == void ? void || alias std::add_rvalue_reference_t<T>)
constexpr explicit ( std : : convertible_to < std : : add_rvalue_reference_t < T > , Option < T > > ) Option & operator = ( _details : : rv_ref_t < T > value ) noexcept ( std : : is_void_v < T > | | std : : is_nothrow_constructible_v < decltype ( std : : declval < Option < T > > ( ) . inner_ ) , std : : add_rvalue_reference_t < std : : invoke_result_t < decltype ( convert_to_held_rv ) , std : : add_rvalue_reference_t < T > > > > ) requires ( ! std : : is_void_v < T > )
{
using inner_t = std : : remove_reference_t < decltype ( inner_ ) > ;
if constexpr ( ! std : : is_void_v < T > )
inner_ = inner_t { convert_to_held_rv ( std : : forward < decltype ( value ) > ( value ) ) } ;
else inner_ = inner_t { true } ;
return * this ;
}
//accessors
constexpr bool has_value ( ) const noexcept { return inner_ . has_value ( ) ; }
//XXX: We're returning held_type* here, which we don't want to expose...
constexpr const auto * try_get_value ( ) const noexcept { return _details : : map_null ( inner_ . try_get_value ( ) , [ ] ( const auto * ptr ) { return std : : addressof ( convert_from_held_cv ( * ptr ) ) ; } ) ; }
constexpr auto * try_get_value ( ) noexcept { return _details : : map_null ( inner_ . try_get_value ( ) , [ ] ( auto * ptr ) { return std : : addressof ( convert_from_held_ref ( * ptr ) ) ; } ) ; }
//TODO: XXX: We need to `convert_from_held` here!
constexpr const auto & value ( ) const & { if ( const auto * p = try_get_value ( ) ) return convert_from_held_cv ( * p ) ; throw _unwrap_error ( ) ; }
constexpr auto & value ( ) & { if ( auto * p = try_get_value ( ) ) return convert_from_held_ref ( * p ) ; throw _unwrap_error ( ) ; }
constexpr decltype ( auto ) value ( ) & & { if ( auto * p = try_get_value ( ) ) return convert_from_held_rv ( std : : move ( * p ) ) ; throw _unwrap_error ( ) ; }
constexpr auto value ( ) const & & { if ( const auto * p = try_get_value ( ) ) return auto ( convert_from_held_cv ( * p ) ) ; throw _unwrap_error ( ) ; }
constexpr operator bool ( ) const noexcept { return has_value ( ) ; }
constexpr const auto * operator - > ( ) const { return _details : : map_null ( try_get_value ( ) , _details : : identity , [ ] [ [ noreturn ] ] ( ) - > std : : nullptr_t { throw _unwrap_error ( ) ; } ) ; }
constexpr auto * operator - > ( ) { return _details : : map_null ( try_get_value ( ) , _details : : identity , [ ] [ [ noreturn ] ] ( ) - > std : : nullptr_t { throw _unwrap_error ( ) ; } ) ; }
constexpr const auto & operator * ( ) const & { return value ( ) ; }
constexpr auto & operator * ( ) & { return value ( ) ; }
constexpr decltype ( auto ) operator * ( ) & & { return value ( ) ; }
constexpr auto operator * ( ) const & & { return value ( ) ; }
private :
constexpr static decltype ( auto ) _unwrap_error ( ) {
if consteval {
return UnwrapError { } ;
} else {
return std : : runtime_error ( UnwrapError { } . message ( ) ) ;
}
}
struct _impl_base {
typedef _null_opt_impl : : held_type < T > held_type ;
typedef _null_opt_impl : : used_type < T > type ;
//struct none_type {};
typedef none_t none_type ;
constexpr _impl_base ( ) noexcept = default ;
constexpr ~ _impl_base ( ) noexcept = default ;
} ;
template < typename _U = T , bool O = is_null_optimised > struct _impl ;
template < typename _U , bool O > struct _impl_type { using type = _impl < _U , O > ; } ;
template < bool O > struct _impl_type < void , O > {
static_assert ( O , " void should be `null_opt` " ) ;
/// std::is_void_v<T>
struct alignas ( bool ) type final : public _impl_base {
using held_type = _impl_base : : held_type ;
using none_type = _impl_base : : none_type ;
constexpr type ( std : : nullptr_t ) noexcept : _impl_base ( ) , has ( false ) { }
constexpr explicit type ( bool & & value ) noexcept : _impl_base ( ) , has ( value ) { }
constexpr ~ type ( ) noexcept { }
constexpr bool has_value ( ) const noexcept { return has ; }
constexpr const bool * try_get_value ( ) const noexcept { return & has ; }
constexpr bool * try_get_value ( ) noexcept { return & has ; }
constexpr bool get_value ( ) const noexcept { return has ; }
constexpr bool & get_value ( ) noexcept { return has ; }
bool has ;
} ;
} ;
/// null_opt<T> == true
template < typename _U > struct alignas ( _U ) _impl < _U , true > final : public _impl_base {
using type_info = null_optimise < T > ;
using held_type = _impl_base : : held_type ;
using none_type = _impl_base : : none_type ;
static_assert ( has_null_opt < T > , " Invalid use of Option::_impl<true> where has_null_opt<T> == false " ) ;
constexpr _impl ( std : : nullptr_t ) noexcept : _impl_base ( ) , value ( type_info : : sentinel ) { }
constexpr _impl ( std : : add_rvalue_reference_t < held_type > value ) noexcept ( std : : is_nothrow_move_constructible_v < held_type > )
: _impl_base ( )
, value ( std : : move ( value ) )
{ }
/*//TODO: XXX: Make Option(T&&) ctor handle calling `convert_to_held` instead? Another template interface for this??
constexpr explicit _impl ( std : : add_rvalue_reference_t < type_info : : type > value ) noexcept ( std : : is_nothrow_constructible_v < held_type , std : : invoke_result_t < type_info : : convert_to_held , decltype ( value ) > > & & std : : is_nothrow_invocable_v < type_info : : convert_to_held , decltype ( value ) ) requires ( ! std : : is_same_v < type_info : : type , held_type > )
: _impl_base ( )
, value ( type_info : : convert_to_held ( std : : forward < decltype ( value ) > ( value ) ) )
{ } */
constexpr _impl & operator = ( std : : add_rvalue_reference_t < held_type > value ) noexcept ( std : : is_nothrow_move_constructible_v < held_type > )
{
std : : exchange ( value , std : : forward < decltype ( value ) > ( value ) ) ;
return * this ;
}
constexpr _impl & operator = ( none_t ) noexcept ( std : : is_nothrow_destructible_v < held_type > )
{
if ( has_value ( ) ) {
using sen_t = decltype ( type_info : : sentinel ) ;
std : : exchange ( value , sen_t { type_info : : sentinel } ) ;
}
return * this ;
}
constexpr ~ _impl ( ) noexcept ( std : : is_nothrow_destructible_v < held_type > ) { }
constexpr bool has_value ( ) const noexcept { return ! bool ( type_info : : sentinel = = value ) ; }
constexpr const held_type * try_get_value ( ) const noexcept { return has_value ( ) ? std : : addressof ( value ) : nullptr ; }
constexpr held_type * try_get_value ( ) noexcept { return has_value ( ) ? std : : addressof ( value ) : nullptr ; }
held_type value ;
} ;
/// null_opt<T> == false
template < typename _U > struct alignas ( _U ) _impl < _U , false > final : public _impl_base {
using held_type = _impl_base : : held_type ;
using none_type = _impl_base : : none_type ;
constexpr _impl ( std : : nullptr_t ) noexcept : _impl_base ( ) , has ( false ) , none ( none_type { } ) { }
constexpr _impl ( std : : add_rvalue_reference_t < held_type > value )
noexcept ( std : : is_nothrow_move_constructible_v < held_type > )
: _impl_base ( )
, has ( true )
, some ( std : : move ( value ) )
{ }
constexpr _impl & operator = ( std : : add_rvalue_reference_t < held_type > value ) noexcept ( std : : is_nothrow_move_constructible_v < held_type > & & std : : is_nothrow_destructible_v < held_type > )
{
if ( has ) std : : exchange ( some , std : : forward < decltype ( value ) > ( value ) ) ;
else {
none . ~ none_type ( ) ;
if constexpr ( noexcept ( std : : is_nothrow_move_constructible_v < held_type > ) )
some = std : : move ( value ) ;
else try {
some = std : : move ( value ) ;
} catch ( . . . ) {
none = none_type { } ;
throw ;
}
has = true ;
}
return * this ;
}
constexpr _impl & operator = ( none_t ) noexcept ( std : : is_nothrow_destructible_v < held_type > )
{
if ( has ) {
some . ~ held_type ( ) ;
none = none_type { } ;
has = false ;
}
return * this ;
}
constexpr ~ _impl ( ) noexcept ( std : : is_nothrow_destructible_v < held_type > ) {
if ( has ) some . ~ held_type ( ) ;
else none . ~ none_type ( ) ;
}
constexpr bool has_value ( ) const noexcept { return has ; }
constexpr const held_type * try_get_value ( ) const noexcept {
return has ? std : : addressof ( some ) : nullptr ;
}
constexpr held_type * try_get_value ( ) noexcept {
return has ? std : : addressof ( some ) : nullptr ;
}
bool has ;
union {
held_type some ;
[ [ no_unique_address ] ] _impl_base : : none_type none ;
} ;
} ;
_impl_type < T , is_null_optimised > : : type inner_ ;
// --- `held_type` convertions
constexpr static decltype ( auto ) convert_to_held_rv ( _details : : rv_ref_t < T > from ) noexcept ( ! is_null_optimised | | std : : is_nothrow_invocable_v < null_optimise < T > : : convert_to_held , _details : : rv_ref_t < T > > ) { if constexpr ( is_null_optimised ) {
return null_optimise < T > : : convert_to_held ( std : : forward < T > ( from ) ) ;
} else {
return std : : forward < decltype ( from ) > ( from ) ;
} }
constexpr static decltype ( auto ) convert_to_held_cv ( _details : : cv_ref_t < T > from ) noexcept ( ! is_null_optimised | | std : : is_nothrow_invocable_v < null_optimise < T > : : convert_to_held , _details : : cv_ref_t < T > > ) { if constexpr ( is_null_optimised ) {
return null_optimise < T > : : convert_to_held ( from ) ;
} else {
return std : : forward < decltype ( from ) > ( from ) ;
} }
using this_held_type = typename std : : remove_reference_t <
decltype ( std : : declval < Option < T > > ( )
. inner_ ) >
: : held_type ;
constexpr static decltype ( auto ) convert_from_held_cv (
_details : : cv_ref_t < this_held_type > held ) noexcept ( ! is_null_optimised | | std : : is_nothrow_invocable_v < null_optimise < T > : : convert_from_held , _details : : cv_ref_t < this_held_type > > ) { if constexpr ( is_null_optimised ) {
return null_optimise < T > : : convert_from_held ( std : : forward < decltype ( held ) > ( held ) ) ;
} else {
return std : : forward < decltype ( held ) > ( held ) ;
} }
constexpr static decltype ( auto ) convert_from_held_ref (
_details : : mut_ref_t < this_held_type > held ) noexcept ( ! is_null_optimised | | std : : is_nothrow_invocable_v < null_optimise < T > : : convert_from_held , _details : : mut_ref_t < this_held_type > > ) { if constexpr ( is_null_optimised ) {
return null_optimise < T > : : convert_from_held ( std : : forward < decltype ( held ) > ( held ) ) ;
} else {
return std : : forward < decltype ( held ) > ( held ) ;
} }
constexpr static decltype ( auto ) convert_from_held_rv (
_details : : rv_ref_t < this_held_type > held ) noexcept ( ! is_null_optimised | | std : : is_nothrow_invocable_v < null_optimise < T > : : convert_from_held , _details : : rv_ref_t < this_held_type > > ) { if constexpr ( is_null_optimised ) {
return null_optimise < T > : : convert_from_held ( std : : forward < decltype ( held ) > ( held ) ) ;
} else {
return std : : forward < decltype ( held ) > ( held ) ;
} }
// ---
} ;
template < typename T , typename U > requires ( requires ( T & & p ) { static_cast < U & & > ( p ) ; } )
constexpr Option < U > static_opt_cast ( Option < T > & & p ) noexcept
{
if ( bool ( p ) ) return Option < U > { static_cast < U & & > ( std : : move ( * p ) ) } ;
return Option < U > { nullptr } ;
}
template < typename T , typename U > requires ( requires ( T & & p ) { static_cast < U & & > ( p ) ; } )
constexpr Option < U > dynamic_opt_cast ( Option < T > & & p )
{
if ( T * ptr = p . try_get_value ( ) )
if ( U * pv = dynamic_cast < U * > ( ptr ) )
return Option < U > { std : : move ( * pv ) } ;
return Option < U > { nullptr } ;
}
static_assert ( sizeof ( Option < void * > ) > sizeof ( void * ) , " Option<T*>: Bad null_opt size " ) ;
static_assert ( alignof ( Option < void * > ) = = alignof ( void * ) , " Option<T*>: Bad null_opt align " ) ;
static_assert ( sizeof ( Option < ptr : : NonNull < int > > ) = = sizeof ( int * ) , " Option<NonNull<T>>: Bad null_opt size " ) ;
static_assert ( alignof ( Option < ptr : : NonNull < int > > ) = = alignof ( int * ) , " Option<NonNull<T>>: Bad null_opt align " ) ;
static_assert ( sizeof ( Option < ptr : : Unique < int > > ) = = sizeof ( int * ) , " Option<Unique<T>>: Bad null_opt size " ) ;
static_assert ( alignof ( Option < ptr : : Unique < int > > ) = = alignof ( int * ) , " Option<Unique<T>>: Bad null_opt align " ) ;
static_assert ( sizeof ( Option < double & > ) = = sizeof ( double & ) , " Option<T&>: Bad null_opt size " ) ;
static_assert ( alignof ( Option < double & > ) = = alignof ( double * ) , " Option<T&>: Bad null_opt align " ) ;
static_assert ( sizeof ( Option < long long int & & > ) = = sizeof ( char * ) , " Option<T&&>: Bad null_opt size " ) ;
static_assert ( alignof ( Option < long long int & & > ) = = alignof ( long * ) , " Option<T&&>: Bad null_opt align " ) ;
static_assert ( alignof ( Option < int > ) = = alignof ( int ) , " Option<int>: Bad non null_opt align " ) ;
static_assert ( sizeof ( Option < void > ) = = sizeof ( bool ) , " Option<void>: Bad size " ) ;
static_assert ( alignof ( Option < void > ) = = alignof ( bool ) , " Option<void>: Bad align " ) ;
}
using optional : : Option ;
}