@ -10,7 +10,6 @@
# include "exopt.h"
# include "exopt.h"
namespace exopt : : types { namespace boxed {
namespace exopt : : types { namespace boxed {
template < typename T >
template < typename T >
struct [ [ gnu : : visibility ( " internal " ) ] ] boxable_value_type { using type = std : : conditional_t
struct [ [ gnu : : visibility ( " internal " ) ] ] boxable_value_type { using type = std : : conditional_t
< std : : is_reference_v < T >
< std : : is_reference_v < T >
@ -31,7 +30,7 @@ namespace exopt::types { namespace boxed {
requires ( std : : is_copy_constructible_v < T > )
requires ( std : : is_copy_constructible_v < T > )
{ if ( __builtin_expect ( ! bool ( c ) , false ) )
{ if ( __builtin_expect ( ! bool ( c ) , false ) )
# ifdef DEBUG
# ifdef DEBUG
throw ptr : : null_e xception< T > { } ;
throw ptr : : NullDerefE xception< T > { } ;
# else
# else
__builtin_unreachable ( ) ;
__builtin_unreachable ( ) ;
# endif
# endif
@ -63,17 +62,8 @@ namespace exopt::types { namespace boxed {
{ static_cast < std : : decay_t < To > & & > ( from ) } - > std : : convertible_to < To > ;
{ static_cast < std : : decay_t < To > & & > ( from ) } - > std : : convertible_to < To > ;
} ;
} ;
struct ErasedTypeDeleter {
constexpr virtual ~ ErasedTypeDeleter ( ) = default ;
constexpr ErasedTypeDeleter ( ) noexcept = default ;
constexpr virtual void apply_delete ( ) = 0 ;
//TODO: See below...
} ;
template < polymorphic_castable < void > T >
template < polymorphic_castable < void > T >
constexpr auto uniq_erase_type ( std : : unique_ptr < T > & & from ) noexcept {
constexpr auto uniq_erase_type ( std : : unique_ptr < T > & & from ) noexcept {
//TODO: Overload case for `unique_ptr<T, D>` for `deleter` to apply (and contain) `D()` on back_cast()ed pointer T* instead of assuming default `delete`.
struct deleter final /*: public ErasedTypeDeleter XXX <- Is this useful, for unerasing the type, maybe?*/ {
struct deleter final /*: public ErasedTypeDeleter XXX <- Is this useful, for unerasing the type, maybe?*/ {
typedef T type ;
typedef T type ;
@ -94,18 +84,26 @@ namespace exopt::types { namespace boxed {
delete static_cast < T * > ( p ) ;
delete static_cast < T * > ( p ) ;
}
}
constexpr T * back_cast ( void * p ) noexcept { return static_cast < T * > ( p ) ; }
constexpr T * back_cast ( void * p ) noexcept { return static_cast < T * > ( p ) ; }
/*constexpr deleter() noexcept = default;
constexpr deleter ( ) noexcept = default ;
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 > {
return std : : unique_ptr < void , deleter > {
/ / XXX : Cannot retain type information of most - derived class from this : \
/ / Cannot retain type information of most - derived class from this : \
dynamic_cast < void * >
dynamic_cast < void * >
static_cast < void * >
static_cast < void * >
( std : : move ( from ) . release ( ) )
( std : : move ( from ) . release ( ) )
} ;
} ;
}
}
template < polymorphic_castable < void > T , typename D > requires ( requires { typename std : : unique_ptr < T , D > ; } )
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 {
constexpr auto uniq_erase_type ( std : : unique_ptr < T , D > & & from ) noexcept {
class deleter final {
class deleter final {
D m_del ;
D m_del ;
@ -127,6 +125,7 @@ namespace exopt::types { namespace boxed {
constexpr deleter & operator = ( const deleter & ) noexcept ( std : : is_nothrow_copy_assignable_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 & 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 > ) { }
constexpr ~ deleter ( ) noexcept ( std : : is_nothrow_destructible_v < D > ) { }
} ;
} ;
if constexpr ( std : : is_default_constructible_v < D > )
if constexpr ( std : : is_default_constructible_v < D > )
@ -166,72 +165,8 @@ namespace exopt::types { namespace boxed {
else return static_uniq_cast ( std : : move ( from ) ) ;
else return static_uniq_cast ( std : : move ( from ) ) ;
}
}
#if 0
//TODO: Cannot be implemented with this method
constexpr bool is_boxed_value ( auto const & value ) noexcept {
return bool ( dynamic_cast < ObjectBase const * > ( std : : addressof ( value ) ) ) ;
}
template < typename T >
constexpr bool is_boxed_type ( ) noexcept {
using type = std : : remove_reference_t < T > ;
constexpr type const * VALUE = nullptr ;
return bool ( dynamic_cast < ObjectBase const * > ( VALUE ) ) ;
}
/ / We need either : Tagged pointers ( XOR , bit - mangled , manual heap , or GOT ( external allocation ) kind ) , \
or ( preferred ) : layout of Box < T > to be ` { aligned < T > ( T , box_type_tag ) } unique * ` \
box_type_tag should be : One pointer width , starting at offset * T + 1 ( aligned to T within internal tuple struct ) , provides a way to access RTTI for ` * T ` , a pointer to static ( non - allocated ) type information or dynamic_cast used lambda would be ideal .
# endif
/// Base class which is used to store the layout of Box<T>'s allocated memory. Intended for use to determine if a certain `T`'s `this` pointer is inside a `Box<T>` or not.
class dynamic_boxed_layout_base {
using Self = dynamic_boxed_layout_base ;
public :
constexpr dynamic_boxed_layout_base ( ) noexcept = default ;
constexpr ~ dynamic_boxed_layout_base ( ) = default ;
typedef void * ( dynamic_cast_t ) ( Self * ) ;
typedef const void * ( dynamic_const_cast_t ) ( Self const * ) ;
constexpr virtual size_t value_byte_offset ( ) const noexcept = 0 ;
[ [ gnu : : returns_nonnull ] ]
constexpr void * extract_untyped_pointer ( ) noexcept {
return reinterpret_cast < void * > (
reinterpret_cast < this > ( unsigned char * ) + value_byte_offset ( )
) ;
}
[ [ gnu : : returns_nonnull ] ]
constexpr const void * extract_untyped_pointer ( ) const noexcept {
return reinterpret_cast < void const * > (
reinterpret_cast < this > ( const unsigned char * ) + value_byte_offset ( )
) ;
}
constexpr virtual void * invoke_casting_shim ( dynamic_cast_t const * ) const noexcept = 0 ;
template < typename T >
constexpr bool is_typeof ( ) const noexcept { return invoke_casting_shim ( [ ] ( Self * self ) { return dynamic_cast < T * > ( self )
template < typename T >
[ [ gnu : : returns_nonnull ] ]
inline T * extract_pointer_unsafe ( ) noexcept {
return reinterpret_cast < T * > ( extract_untyped_pointer ( ) ) ;
}
template < typename T >
[ [ gnu : : returns_nonnull ] ]
inline const T * extract_pointer_unsafe ( ) const noexcept {
return reinterpret_cast < const T * > ( extract_untyped_pointer ( ) ) ;
}
template < typename T >
[ [ gnu : : returns_nonnull ] ]
inline T * try_extract_pointer ( ) noexcept {
return is_typeof < T > ( ) ? extract_pointer_unsafe < T > ( ) : nullptr ;
}
} ;
template < typename T >
template < typename T >
struct Box final {
struct alignas ( std : : unique_ptr < T > ) Box {
typedef boxable_value_type < T > : : type type ;
typedef boxable_value_type < T > : : type type ;
template < std : : derived_from < T > U , bool RuntimeCheck = false >
template < std : : derived_from < T > U , bool RuntimeCheck = false >
@ -277,15 +212,24 @@ namespace exopt::types { namespace boxed {
}
}
[ [ gnu : : nonnull ( 1 ) ] ]
[ [ gnu : : nonnull ( 1 ) ] ]
constexpr static Box < T > from_raw_ptr ( T * ptr ) noexcept {
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 } } ;
return Box < T > { std : : unique_ptr < T > { ptr } } ;
}
}
constexpr static Box < T > from_raw_ptr ( std : : unique_ptr < T > & & uniq )
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 DEBUG
{ if ( __builtin_expect ( ! uniq , false ) ) throw ptr : : NullException { } ;
if ( __builtin_expect ( ! uniq , false ) ) ptr: : throw_null_exception < T > ( ) ;
# else
# else
noexcept {
_EO_ASSUME ( uniq . get ( ) ! = nullptr ) ;
_EO_ASSUME ( uniq . get ( ) ! = nullptr ) ;
# endif
# endif
return Box < T > { std : : move ( uniq ) } ;
return Box < T > { std : : move ( uniq ) } ;
@ -300,49 +244,63 @@ namespace exopt::types { namespace boxed {
//TODO: Accessors, `(explicit?) operator std::unique_ptr<T> const&[&]`?, etc.
//TODO: Accessors, `(explicit?) operator std::unique_ptr<T> const&[&]`?, etc.
constexpr ~ Box ( ) = default ;
constexpr ~ Box ( ) = default ;
private :
protected :
constexpr explicit Box ( std : : unique_ptr < T > & & ptr )
// For types that may need to invalidate the Box<T> guarantee without exposing it in API, they can inherit (non-virtual).
: m_ptr ( std : : move ( ptr ) ) {
_EO_ASSUME ( m_ptr . get ( ) ) ;
}
// TODO: Identifying if a value `this` pointer is boxed
struct alignas ( type ) inner_layout_t final : public dynamic_boxed_layout_base {
using boxed_value_t = type ;
constexpr inner_layout_t ( type & & v ) noexcept ( std : : is_nothrow_move_constructible_v < type > )
// For unsafe operations, wether or not to apply checks.
: value ( std : : move ( v ) ) { }
enum unsafe_t {
//TODO: All the ctors, assigns, and operators that can make this newtype wrapper more conveniently useable as a deref-target for accessing `value`.
UNSAFE ,
} ;
enum maybe_unsafe_t {
CHECKED ,
DEBUG_CHECKED ,
} ;
constexpr /*virtual*/ ~ inner_layout_t ( ) = default ;
constexpr Box ( util : : exact_type < unsafe_t > auto u , std : : unique_ptr < T > & & ptr ) noexcept
: m_ptr ( std : : move ( ptr ) ) { ( void ) u ; }
[ [ gnu : : const ] ]
constexpr static Box < T > from_raw_ptr ( unsafe_t u , std : : unique_ptr < T > & & uniq ) noexcept {
constexpr size_t value_byte_offset ( ) const noexcept override { return offsetof ( inner_layout_t , value ) ; }
return Box { u , std : : move ( uniq ) } ;
}
type value ;
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 ; }
struct inner_unique_ptr_t final {
constexpr std : : unique_ptr < T > & & as_unique ( unsafe_t ) & & noexcept { return std : : move ( m_ptr ) ; }
std : : unique_ptr < inner_layout_t >
constexpr std : : unique_ptr < T > const & & as_unique ( unsafe_t ) const & & noexcept { return std : : move ( m_ptr ) ; }
} ;
inner_unique_ptr_t m_ptr ; // Unique<T> 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 ;
}
# define _EO_ADD_RV std::add_rvalue_reference_v
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 ( ) ) ;
}
template < typename Base , typename T = Base >
std : : unique_ptr < T > m_ptr ; // Unique<T> m_ptr;
constexpr inline bool is_boxable_v = binary_convertible < _EO_ADD_RV < T > , _EO_ADD_RV < Base > > and requires ( T & & value ) {
typename Box < Base > ;
typename Box < Base > : : type ;
{ std : : make_unique < T > ( std : : move ( value ) ) } - > std : : same_as < std : : unique_ptr < T > > ;
} ;
} ;
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 > concept is_boxable = is_boxable_v < T > ;
template < typename T >
template < typename T >
constexpr inline bool is_nothrow_boxable_v = is_boxable_v < T > & & std : : is_nothrow_constructible_v < Box < T > , T > ;
constexpr inline bool is_nothrow_boxable_v = is_boxable_v < T > & & std : : is_nothrow_constructible_v < Box < T > , T > ;
# undef _EO_ADD_RV
template < typename T , typename U > requires ( requires ( T & & o ) { static_cast < U & & > ( o ) ; } )
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 ( ) ) ) ; }
constexpr Box < U > static_box_cast ( Box < T > & & b ) noexcept { return Box < U > : : from_raw_ptr ( static_cast < U * > ( b . release ( ) ) ) ; }
@ -356,7 +314,12 @@ namespace exopt::types { namespace boxed {
delete rel ;
delete rel ;
throw ;
throw ;
}
}
} //TODO: Overload for `const&` that does the type check *before* the copy allocation.
}
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
#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 > )
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 > )
@ -399,6 +362,8 @@ namespace exopt::types { namespace boxed {
using boxed : : Box ;
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 " ) ] ] {
namespace exopt : : types { namespace optional [ [ gnu : : visibility ( " internal " ) ] ] {
template < typename T >
template < typename T >
struct null_optimise < boxed : : Box < T > > { constexpr static inline bool value = true ;
struct null_optimise < boxed : : Box < T > > { constexpr static inline bool value = true ;
@ -413,3 +378,4 @@ namespace exopt::types { namespace optional [[gnu::visibility("internal")]] {
}
}
# endif