@ -10,12 +10,15 @@
# 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 ; }
@ -35,10 +38,96 @@ namespace exopt::types { namespace boxed {
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 < 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 ) ) ;
}
#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
template < typename T >
struct Box {
struct Box final {
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 > ( ) } { }
@ -90,9 +179,35 @@ namespace exopt::types { namespace boxed {
_EO_ASSUME ( m_ptr . get ( ) ) ;
}
#if 0
// TODO: Identifying if a value `this` pointer is boxed
struct alignas ( type ) inner_layout {
constexpr inner_layout ( ) noexcept = default ;
constexpr inner_layout ( const inner_layout & ) noexcept = default ;
constexpr ~ inner_layout ( ) noexcept = default ;
type value ;
box_type_tag identifier { box_tag_of < T > ( ) } ;
} ;
# endif
std : : unique_ptr < T > m_ptr ; // Unique<T> m_ptr;
} ;
# define _EO_ADD_RV std::add_rvalue_reference_v
template < typename Base , typename T = Base >
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 > > ;
} ;
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 > ;
# undef _EO_ADD_RV
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 ( ) ) ) ; }
@ -106,6 +221,44 @@ namespace exopt::types { namespace boxed {
throw ;
}
} //TODO: Overload for `const&` that does the type check *before* the copy allocation.
#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 ;
}