# pragma once
# include <memory>
# include <utility>
# include "pointer.h"
# include "util.hh"
# include "exopt.h"
namespace exopt : : types { namespace boxed {
template < typename T >
struct [ [ gnu : : visibility ( " internal " ) ] ] boxable_value_type { using type = std : : conditional_t
< std : : is_reference_v < T >
, std : : reference_wrapper < std : : remove_cvref_t < T > >
, T > ; } ;
namespace {
template < typename T >
constexpr std : : unique_ptr < T > uniq_clone ( std : : unique_ptr < T > const & c ) noexcept ( std : : is_nothrow_copy_constructible_v < T > ) requires ( std : : is_copy_constructible_v < T > )
{ if ( __builtin_expect ( bool ( c ) , true ) ) return std : : make_unique < T > ( * c ) ; return { nullptr } ; }
template < typename T >
constexpr std : : unique_ptr < T > uniq_clone_unsafe ( std : : unique_ptr < T > const & c )
# ifndef DEBUG
noexcept ( std : : is_nothrow_copy_constructible_v < T > )
# endif
requires ( std : : is_copy_constructible_v < T > )
{ if ( __builtin_expect ( ! bool ( c ) , false ) )
# ifdef DEBUG
throw ptr : : NullDerefException < T > { } ;
# else
__builtin_unreachable ( ) ;
# endif
return std : : make_unique < T > ( * c ) ; }
template < typename Error = ptr : : NullException >
constexpr decltype ( auto ) force_ok ( auto & & mv ) requires ( std : : is_convertible_v < decltype ( mv ) , bool > & & std : : is_default_constructible_v < Error > )
{
struct err {
[ [ gnu : : noinline , gnu : : cold , noreturn ] ]
static constexpr void _throw ( ) { throw Error { } ; }
} ;
if ( __builtin_expect ( ! bool ( mv ) , false ) ) err : : _throw ( ) ;
return std : : forward < decltype ( mv ) > ( mv ) ;
}
} // _impl (anon)
template < typename From , typename To >
concept binary_convertible = std : : is_convertible_v < From , To > and std : : is_convertible_v < To , From > ;
template < typename From , typename To >
concept polymorphic_castable = std : : derived_from < std : : decay_t < To > , std : : decay_t < From > >
or std : : derived_from < std : : decay_t < From > , std : : decay_t < To > >
or std : : is_same_v < std : : decay_t < From > , std : : decay_t < To > >
or requires ( std : : decay_t < From > & & from ) {
{ static_cast < std : : decay_t < To > & & > ( from ) } - > std : : convertible_to < To > ;
} ;
template < polymorphic_castable < void > T >
constexpr auto uniq_erase_type ( std : : unique_ptr < T > & & from ) noexcept {
struct deleter final /*: public ErasedTypeDeleter XXX <- Is this useful, for unerasing the type, maybe?*/ {
typedef T type ;
/*[[gnu::cold, noreturn, gnu::noinline]]
constexpr static void _throw ( )
{
throw ptr : : NullException { } ;
}
public : */
constexpr void opreator ( ) ( void * p ) noexcept ( std : : is_nothrow_destructible_v < T > ) {
/* T* ptr;
if constexpr ( DOES_CHECK ) {
if ( __builtin_expect ( ! ( ptr = dynamic_cast < T * > ( p ) ) , false ) )
_throw ( ) ;
} else ptr = static_cast < T * > ( p ) ;
delete ptr ; */
delete static_cast < T * > ( p ) ;
}
constexpr T * back_cast ( void * p ) noexcept { return static_cast < T * > ( p ) ; }
constexpr deleter ( ) noexcept = default ;
constexpr deleter ( deleter const & ) noexcept = default ;
constexpr deleter ( deleter & & ) noexcept = default ;
constexpr deleter & operator = ( deleter const & ) noexcept = default ;
constexpr deleter & operator = ( deleter & & ) noexcept = default ;
constexpr ~ deleter ( ) noexcept = default ;
} ;
return std : : unique_ptr < void , deleter > {
/ / Cannot retain type information of most - derived class from this : \
dynamic_cast < void * >
static_cast < void * >
( std : : move ( from ) . release ( ) )
} ;
}
template < polymorphic_castable < void > T , typename D > requires ( requires {
typename std : : unique_ptr < T , D > ;
requires ( ! std : : is_same_v < std : : unique_ptr < T , D > : : deleter_type , std : : unique_ptr < T > : : deleter_type ) ;
} )
constexpr auto uniq_erase_type ( std : : unique_ptr < T , D > & & from ) noexcept {
class deleter final {
D m_del ;
public :
constexpr void operator ( ) ( void * p ) noexcept ( std : : is_nothrow_invocable_v < D : : operator ( ) , T > ) {
m_del ( static_cast < T * > ( p ) ) ;
}
constexpr T * back_cast ( void * p ) noexcept { return static_cast < T * > ( p ) ; }
constexpr deleter ( D & & deleter ) noexcept ( std : : is_nothrow_move_constructible_v < D > )
: m_del ( std : : move ( deleter ) ) { }
constexpr deleter ( ) noexcept ( std : : is_nothrow_default_constructible_v < D > ) requires ( std : : is_default_constructible_v < D > ) = default ; //: m_del() {}
constexpr deleter ( const deleter & ) noexcept ( std : : is_nothrow_copy_constructible_v < D > ) = default ;
constexpr deleter ( deleter & & ) noexcept ( std : : is_nothrow_move_constructible_v < D > ) = default ;
constexpr deleter & operator = ( const deleter & ) noexcept ( std : : is_nothrow_copy_assignable_v < D > ) = default ;
constexpr deleter & operator = ( deleter & & ) noexcept ( std : : is_nothrow_move_assignable_v < D > ) = default ;
constexpr ~ deleter ( ) noexcept ( std : : is_nothrow_destructible_v < D > ) requires ( std : : is_trivially_destructible_v < D > ) = default ;
constexpr ~ deleter ( ) noexcept ( std : : is_nothrow_destructible_v < D > ) { }
} ;
if constexpr ( std : : is_default_constructible_v < D > )
return std : : unique_ptr < void , deleter > {
static_cast < void * >
( std : : move ( from ) . release ( ) ) , deleter ( )
} ;
else {
deleter & & del { std : : move ( from . get_deleter ( ) ) } ;
return std : : unique_ptr < void , deleter > {
static_cast < void * >
( std : : move ( from ) . release ( ) ) , deleter { std : : move ( del ) }
} ;
}
}
template < typename To , typename From > requires ( polymorphic_castable < From , To > )
constexpr std : : unique_ptr < To > static_uniq_cast ( std : : unique_ptr < From > & & from ) noexcept
{
return std : : unique_ptr < To > {
static_cast < To * > ( std : : move ( from ) . release ( ) )
} ;
}
template < typename To , typename From > requires ( polymorphic_castable < From , To > )
constexpr std : : unique_ptr < To > dynamic_uniq_cast ( std : : unique_ptr < From > & & from ) noexcept
{
return std : : unique_ptr < To > {
dynamic_cast < To * > ( std : : move ( from ) . release ( ) )
} ;
}
template < typename To , typename From , bool Check = false > requires ( polymorphic_castable < From , To > )
constexpr std : : unique_ptr < To > uniq_cast ( std : : unique_ptr < From > & & from ) noexcept
{
if constexpr ( Check ) return dynamic_uniq_cast ( std : : move ( from ) ) ;
else return static_uniq_cast ( std : : move ( from ) ) ;
}
template < typename T >
struct alignas ( std : : unique_ptr < T > ) Box {
typedef boxable_value_type < T > : : type type ;
template < std : : derived_from < T > U , bool RuntimeCheck = false >
constexpr static Box < T > new_dynamic ( U & & v ) noexcept ( ! RuntimeCheck ) {
if constexpr ( ! RuntimeCheck ) {
auto boxed = std : : make_unique < U > ( std : : move ( v ) ) ;
return Box < T > { std : : unique_ptr < T > { static_cast < T * > ( boxed . release ( ) ) } } ;
} else
return Box < T > { std : : make_unique < T > ( dynamic_cast < T & & > ( std : : move ( v ) ) ) } ;
}
template < std : : derived_from < T > U , bool Check = false >
constexpr Box < U > upcast ( ) & & noexcept ( ! Check )
{ return Box < U > { force_ok ( uniq_cast < U , T , Check > ( std : : move ( m_ptr ) ) ) } ; }
template < typename U , bool Check = true > requires ( std : : derived_from < T , U > )
constexpr Box < U > downcast ( ) & & noexcept ( ! Check )
{ return Box < U > { force_ok ( uniq_cast < U , T , Check > ( std : : move ( m_ptr ) ) ) } ; }
template < polymorphic_castable < T > U , bool Check = ! std : : derived_from < U , T > > // Default dynamic check set to false only if we can statically determine that T is derived from U and the conversion is therfore infallible
constexpr Box < U > polycast ( ) & & noexcept ( ! Check )
{ return Box < U > { force_ok ( uniq_cast < U , T , Check > ( std : : move ( m_ptr ) ) ) } ; }
constexpr Box ( ) noexcept ( std : : is_nothrow_default_constructible_v < T > ) requires ( std : : is_default_constructible_v < T > )
: m_ptr { std : : make_unique < T > ( ) } { }
template < typename . . . Args > requires ( std : : is_constructible_v < T , Args . . . > )
constexpr Box ( Args & & . . . ctor ) noexcept ( std : : is_nothrow_constructible_v < T , Args . . . > )
: m_ptr { std : : make_unique < T > ( std : : forward < Args > ( ctor ) . . . ) } { }
constexpr Box ( T & & value ) noexcept ( std : : is_nothrow_constructible_v < T > )
: m_ptr { std : : make_unique < T > ( std : : move ( value ) ) } { }
constexpr Box ( Box & & ) noexcept = default ;
constexpr Box & operator = ( Box & & ) noexcept = default ;
constexpr Box ( const Box & copy ) noexcept ( std : : is_nothrow_copy_constructible_v < T > ) requires ( std : : is_copy_constructible_v < T > )
: m_ptr { uniq_clone_unsafe ( copy . m_ptr ) } { }
constexpr Box & operator = ( Box const & copy ) noexcept ( std : : is_nothrow_copy_assignable_v < T > ) requires ( std : : is_copy_assignable_v < T > )
{
if ( __builtin_expect ( this ! = std : : addressof ( copy ) , true ) ) {
m_ptr = uniq_clone_unsafe ( copy . m_ptr ) ;
} return * this ;
}
[ [ gnu : : nonnull ( 1 ) ] ]
constexpr static Box < T > from_raw_ptr ( T * ptr ) noexcept ( ! _EO ( DEBUG ) ) {
# if DEBUG
ptr : : throw_if_null ( ptr ) ;
# endif
return Box < T > { std : : unique_ptr < T > { ptr } } ;
}
constexpr static Box < T > from_raw_ptr ( std : : unique_ptr < T > & & uniq ) {
if ( __builtin_expect ( ! bool ( uniq ) , false ) ) ptr : : throw_null_exception < T > ( ) ;
_EO_ASSUME ( uniq . get ( ) ! = nullptr ) ;
return Box < T > { std : : move ( uniq ) } ;
}
constexpr static Box < T > from_raw_ptr_unchecked ( std : : unique_ptr < T > & & uniq ) noexcept ( ! _EO ( DEBUG ) ) {
# if DEBUG
if ( __builtin_expect ( ! uniq , false ) ) ptr : : throw_null_exception < T > ( ) ;
# else
_EO_ASSUME ( uniq . get ( ) ! = nullptr ) ;
# endif
return Box < T > { std : : move ( uniq ) } ;
}
[ [ gnu : : returns_nonnull ] ]
constexpr T * get ( ) const noexcept { return m_ptr . get ( ) ; }
[ [ gnu : : returns_nonnull ] ]
constexpr T * release ( ) noexcept { return m_ptr . release ( ) ; }
constexpr std : : unique_ptr < T > & & into_unique ( ) & & noexcept { return std : : move ( m_ptr ) ; }
//TODO: Accessors, `(explicit?) operator std::unique_ptr<T> const&[&]`?, etc.
constexpr ~ Box ( ) = default ;
protected :
// For types that may need to invalidate the Box<T> guarantee without exposing it in API, they can inherit (non-virtual).
// For unsafe operations, wether or not to apply checks.
enum unsafe_t {
UNSAFE ,
} ;
enum maybe_unsafe_t {
CHECKED ,
DEBUG_CHECKED ,
} ;
constexpr Box ( util : : exact_type < unsafe_t > auto u , std : : unique_ptr < T > & & ptr ) noexcept
: m_ptr ( std : : move ( ptr ) ) { ( void ) u ; }
constexpr Box ( util : : exact_type < unsafe_t > auto u , Box const & copy ) noexcept
: m_ptr ( uniq_clone ( copy . as_unique ( u ) ) ) { }
constexpr static Box < T > from_raw_ptr ( unsafe_t u , std : : unique_ptr < T > & & uniq ) noexcept {
return Box { u , std : : move ( uniq ) } ;
}
constexpr std : : unique_ptr < T > & as_unique ( unsafe_t ) & noexcept { return m_ptr ; }
constexpr std : : unique_ptr < T > const & as_unique ( unsafe_t ) const & noexcept { return m_ptr ; }
constexpr std : : unique_ptr < T > & & as_unique ( unsafe_t ) & & noexcept { return std : : move ( m_ptr ) ; }
constexpr std : : unique_ptr < T > const & & as_unique ( unsafe_t ) const & & noexcept { return std : : move ( m_ptr ) ; }
constexpr T * as_unsafe_ptr ( unsafe_t ) const noexcept { return m_ptr . get ( ) ; }
constexpr std : : unique_ptr < T > swap_unsafe_ptr ( unsafe_t u , T * raw ) noexcept
{ return ( void ) u ; std : : exchange ( std : : move ( m_ptr ) , std : : unique_ptr < T > { raw } ) ; }
constexpr void set_unsafe_ptr ( unsafe_t 8 , T * raw ) noexcept {
( void ) u ;
m_ptr . reset ( raw ) ;
}
constexpr decltype ( auto ) set_unsafe ( unsafe_t u , std : : unique_ptr < T > & & ptr ) noexcept
{ ( void ) u ; return std : : exchange ( std : : move ( m_ptr ) , std : : move ( ptr ) ) ; }
constexpr auto & swap_unsafe ( unsafe_t , std : : unique_ptr < T > & ptr ) noexcept {
using std : : swap ;
swap ( m_ptr , ptr ) ;
return m_ptr ;
}
constexpr T * release_unsafe ( unsafe_t ) noexcept { return m_ptr . release ( ) ; }
private :
constexpr explicit Box ( std : : unique_ptr < T > & & ptr ) noexcept
: m_ptr ( std : : move ( ptr ) ) {
_EO_ASSUME ( m_ptr . get ( ) ) ;
}
std : : unique_ptr < T > m_ptr ; // Unique<T> m_ptr;
} ;
static_assert ( util : : shares_layout < Box < _Complex double > , _Complex double * > , " Invalid Box<T> memory layout: More than just T* " ) ;
static_assert ( util : : shares_layout < Box < _Complex double > , std : : unique_ptr < _Complex double > > , " Invalid Box<T> memory layout: More than just std::unique_ptr<T> " ) ;
template < typename T > concept is_boxable = is_boxable_v < T > ;
template < typename T >
constexpr inline bool is_nothrow_boxable_v = is_boxable_v < T > & & std : : is_nothrow_constructible_v < Box < T > , T > ;
template < typename T , typename U > requires ( requires ( T & & o ) { static_cast < U & & > ( o ) ; } )
constexpr Box < U > static_box_cast ( Box < T > & & b ) noexcept { return Box < U > : : from_raw_ptr ( static_cast < U * > ( b . release ( ) ) ) ; }
template < typename T , typename U > requires ( requires ( T & & o ) { static_cast < U & & > ( o ) ; } )
constexpr Box < U > dynamic_box_cast ( Box < T > & & b ) {
auto * rel = b . release ( ) ;
try {
return Box < U > : : from_raw_ptr ( std : : addressof ( dynamic_cast < U & & > ( std : : move ( * rel ) ) ) ) ;
} catch ( . . . ) {
delete rel ;
throw ;
}
}
template < typename T , typename U > requires ( requires ( T const & o ) { static_cast < U const & > ( o ) ; } )
constexpr Box < U > dynamic_box_cast ( Box < T > const & b ) {
return Box < U > : : from_raw_ptr ( std : : addressof ( dynamic_cast < U const & > ( * b ) ) ) ;
}
#if 0
template < typename T , typename R /*XXX: Maybe remove this, and put the requires after the definition, using `auto&& value` instead of `R&& value`*/ > requires ( std : : derived_from < R , T > )
constexpr Box < T > box ( R & & value , util : : comptime < bool > auto Check = _EO_CONSTANT_VALUE ( false ) ) noexcept ( std : : is_nothrow_invocable_v < Box < T > : : new_dynamic < R , util : : comptime_eval_v < bool , Check > > ) //XXX: This seems illegal...
{ return Box < T > : : template new_dynamic < R , Check > ( std : : move ( value ) ) ; }
template < typename T >
constexpr Box < T > box ( T & & value ) noexcept ( std : : is_nothrow_constructible_v < Box < T > , T > )
{ return { std : : move ( value ) } ; }
# endif
template < typename T >
constexpr Box < T > box ( T & & value ) noexcept ( std : : is_nothrow_constructible_v < Box < T > , decltype ( value ) > )
{
/*if constexpr(requires(decltype(value) v) { static_cast<T&&>(std::move(v)); }) {
return Box < T > : : template new_dynamic < T > ( std : : move ( value ) ) ;
} else */ return Box < T > { std : : move ( value ) } ;
}
template < typename T >
constexpr Box < T > box ( std : : unique_ptr < T > & & value ) noexcept { return Box < T > { std : : move ( value ) } ; }
template < typename Base , typename T > /*requires(polymorphic_castable<Base, T>)
constexpr Box < Base > polybox ( T & & value ) noexcept
{
return Box < T > { std : : move ( value ) } . polycast < Base > ( )
} */ requires ( std : : derived_from < T , Base > )
constexpr Box < Base > polybox ( T & & value ) noexcept {
return Box < T > { std : : move ( value ) } . polycast < Base , false > ( ) ; // Convertion from derived to base infallible, no need for check.
}
template < typename Base , typename T > /*requires(polymorphic_castable<Base, T>)
constexpr Box < Base > polybox ( T & & value ) noexcept
{
return Box < T > { std : : move ( value ) } . polycast < Base > ( )
} */ requires ( std : : derived_from < T , Base > )
constexpr Box < Base > polybox ( std : : unique_ptr < T > & & value ) noexcept {
return Box < T > { std : : move ( value ) } . polycast < Base , false > ( ) ; // Convertion from derived to base infallible, no need for check.
}
}
using boxed : : Box ;
}
//TODO: See phone notes on correctly implementing the nullopt for Box<T>
#if 0
namespace exopt : : types { namespace optional [ [ gnu : : visibility ( " internal " ) ] ] {
template < typename T >
struct null_optimise < boxed : : Box < T > > { constexpr static inline bool value = true ;
//XXX: Eh.. Idk if Option<T>'s lifetime design can make this work...
using held_type = boxed : : Box < T > ;
using type = boxed : : Box < T > & ;
constexpr static decltype ( auto ) convert_to_held ( std : : add_rvalue_reference_t < type > t ) noexcept { return std : : move ( t ) ; }
constexpr static decltype ( auto ) convert_from_held ( std : : add_rvalue_reference_t < held_type > t ) noexcept { return std : : forward < type > ( t ) ; }
constexpr static inline T * sentinel = nullptr ;
} ;
}
# endif