@ -2,6 +2,7 @@
# include <utility>
# include <stdexcept>
# include <typeinfo>
# define $_oe_msg(msg) { inline const char* what() const noexcept override { return (msg); } }
struct opaque_exception : std : : exception $ _oe_msg ( " unspecified opaque_handle related error " ) ;
@ -19,15 +20,34 @@ struct opaque_handle_impl {
virtual constexpr ~ opaque_handle_impl ( ) { }
template < typename To , bool Reinterpret = false >
inline constexpr const To * unsafe_cast ( ) const noexcept {
if constexpr ( Reinterpret )
return reinterpret_cast < const To * > ( as_raw_ptr ( ) ) ;
else return static_cast < const To * > ( as_raw_ptr ( ) ) ;
}
template < typename To , bool Reinterpret = false >
inline constexpr To * unsafe_cast ( ) noexcept {
if constexpr ( Reinterpret )
return reinterpret_cast < To * > ( as_raw_ptr ( ) ) ;
else return static_cast < To * > ( as_raw_ptr ( ) ) ;
}
template < typename To >
inline constexpr const To * try_cast ( ) const noexcept { return static_cast < const To * > ( as_raw_ptr ( ) ) ; }
inline constexpr const To * try_cast ( ) const noexcept { return is_type ( typeid ( To ) ) ? static_cast < const To * > ( as_raw_ptr ( ) ) : nullptr ; }
template < typename To >
inline constexpr To * try_cast ( ) noexcept { return static_cast < To * > ( as_raw_ptr ( ) ) ; }
inline constexpr To * try_cast ( ) noexcept { return is_type ( typeid ( To ) ) ? static_cast < To * > ( as_raw_ptr ( ) ) : nullptr ; }
constexpr virtual bool is_type ( const std : : type_info & a ) const noexcept {
if ( const auto * p = get_type_info ( ) ) return * p = = a ;
return false ; // Types are uncomparable.
}
protected :
constexpr virtual void * as_raw_ptr ( ) const noexcept = 0 ;
constexpr virtual opaque_handle_impl * try_clone ( ) const = 0 ;
constexpr virtual const std : : type_info * get_type_info ( ) const noexcept = 0 ;
inline virtual opaque_handle_impl & clone ( ) const {
if ( auto * np = try_clone ( ) ) return * np ;
@ -38,6 +58,12 @@ protected:
} ;
struct opaque_handle final {
enum cast_kind {
CAST_SAFE ,
CAST_UNSAFE ,
CAST_UNSAFE_REINTERPRET ,
} ;
constexpr explicit opaque_handle ( opaque_handle_impl * ptr ) noexcept : _impl ( ptr ) { }
constexpr opaque_handle ( opaque_handle & & move ) noexcept
@ -55,32 +81,56 @@ struct opaque_handle final {
constexpr const void * as_ptr ( ) const noexcept { return _impl ? _impl - > as_raw_ptr ( ) : nullptr ; }
constexpr void * as_ptr ( ) noexcept { return _impl ? _impl - > as_raw_ptr ( ) : nullptr ; }
template < typename T , bool Reinterpret = false >
constexpr const T * unsafe_cast ( ) const noexcept { return _impl ? _impl - > unsafe_cast < T , Reinterpret > ( ) : nullptr ; }
///XXX: These don't work as expected. And we can't dynamic_cast<>(void*), so... What do we do to check the type of `as_ptr()` is `T` without embedding all this extra type info? Another layer of inheritence, and have `as_raw_ptr()` return a pointer to that base instead of `void*`? idk...
template < typename T >
/// WARNING: Unsafe, see above
constexpr const T * try_cast ( ) const noexcept { return static_cast < const T * > ( as_ptr ( ) ) ; }
template < typename T >
/// WARNING: Unsafe, see above
constexpr T * try_cast ( ) noexcept { return static_cast < T * > ( as_ptr ( ) ) ; }
template < typename T , bool Reinterpret = false >
constexpr T * unsafe_cast ( ) noexcept { return _impl ? _impl - > unsafe_cast < T , Reinterpret > ( ) : nullptr ; }
template < typename T >
inline const T & cast ( ) const { return * ( try_cast < T > ( ) ? : th row opaque_bad_cast { } ) ; }
constexpr const T * try_cast ( ) const noexcept { return _impl ? _impl - > try_cast < T > ( ) : nullptr ; }
template < typename T >
inline T & cast ( ) { return * ( try_cast < T > ( ) ? : throw opaque_bad_cast { } ) ; }
constexpr T * try_cast ( ) noexcept { return _impl ? _impl - > try_cast < T > ( ) : nullptr ; }
template < typename T , cast_kind Kind = CAST_SAFE >
inline const T & cast ( ) const {
if constexpr ( Kind > = CAST_UNSAFE )
return * ( unsafe_cast < T , ( Kind = = CAST_UNSAFE_REINTERPRET ) > ( ) ? : throw opaque_bad_cast { } ) ;
else
return * ( try_cast < T > ( ) ? : throw opaque_bad_cast { } ) ;
}
template < typename T , cast_kind Kind = CAST_SAFE >
inline T & cast ( ) {
if constexpr ( Kind > = CAST_UNSAFE )
return * ( unsafe_cast < T , ( Kind = = CAST_UNSAFE_REINTERPRET ) > ( ) ? : throw opaque_bad_cast { } ) ;
else
return * ( try_cast < T > ( ) ? : throw opaque_bad_cast { } ) ;
}
constexpr bool has_value ( ) const noexcept { return bool ( _impl ) ; }
constexpr explicit operator bool ( ) const noexcept { return has_value ( ) ; }
//TODO: Test these, we may need to do the same thing we did for `operator*`, but with a shim that has `operator T*` and `operator->` instead of `operator T&`.
template < typename T >
constexpr const T * operator - > ( ) const noexcept { return try_cast < T > ( ) ; }
constexpr const T * operator - > ( ) const noexcept ( std : : is_void_v < T > ) {
if constexpr ( std : : is_void_v < T > )
return as_ptr ( ) ;
else return std : : addressof ( cast < T > ( ) ) ;
}
template < typename T >
constexpr T * operator - > ( ) noexcept { return try_cast < T > ( ) ; } //XXX: TODO: throw if try_cast<T>() is null and T is not `void`.
constexpr T * operator - > ( ) noexcept ( std : : is_void_v < T > )
{
if constexpr ( std : : is_void_v < T > )
return as_ptr ( ) ;
else return std : : addressof ( cast < T > ( ) ) ;
}
inline auto operator * ( ) const noexcept {
[[gnu::artificial]]
inline constexpr auto operator * ( ) const noexcept {
return _impl_deref_const { * this } ;
}
inline auto operator * ( ) noexcept {
[[gnu::artificial]]
inline constexpr auto operator * ( ) noexcept {
return _impl_deref { * this } ;
}
/*
@ -97,12 +147,14 @@ private:
const opaque_handle & ptr ;
template < typename T >
[[gnu::artificial, gnu::always_inline]]
inline operator const T & ( ) & & {
return * ( ptr . try_ cast< T > ( ) ? : throw opaque_bad_cast { } ) ;
return ptr . cast< T > ( ) ;
}
template < typename T >
inline operator const T & ( ) const & & {
return * ( ptr . try_cast < T > ( ) ? : throw opaque_bad_cast { } ) ;
[[gnu::artificial, gnu::always_inline]]
inline operator T ( ) const & & { // For this to work, this method must return `T`, i'm not sure *why*, but oh well. It doesn't seem to incur any extra overhead, so... okay.
return ptr . cast < T > ( ) ;
}
} ;
struct _impl_deref {
@ -110,11 +162,11 @@ private:
template < typename T >
inline operator T & ( ) & & {
return * ( ptr . try_ cast< T > ( ) ? : throw opaque_bad_cast { } ) ;
return ptr . cast< T > ( ) ; //*(ptr.try_cast<T>() ?: throw opaque_bad_cast{});
}
template < typename T >
inline operator const T & ( ) const & & {
return * ( ptr . try_ cast< T > ( ) ? : throw opaque_bad_cast { } ) ;
return ptr . cast< T > ( ) ; //*(ptr.try_cast<T>() ?: throw opaque_bad_cast{});
}
} ;
opaque_handle_impl * _impl ;
@ -138,6 +190,7 @@ protected:
return new opaque_handle_object < T > { T { _obj } } ;
} else return nullptr ;
}
inline constexpr const std : : type_info * get_type_info ( ) const noexcept override { return & typeid ( T ) ; }
private :
T _obj ;
} ;
@ -195,7 +248,8 @@ constexpr inline opaque_handle make_opaque_handle(T* data, const HandleF& handle
{ return const_cast < T * > ( data ) ; }
constexpr opaque_handle_impl * try_clone ( ) const override final
{ return data ? new object_handler ( * this ) : nullptr ; }
inline constexpr const std : : type_info * get_type_info ( ) const noexcept override final { return & typeid ( T ) ; }
T * data ;
const HandleF & handler ;
} ;
@ -240,6 +294,7 @@ constexpr inline opaque_handle make_opaque_handle(T* data, HandleF&& handler) no
return data ? new object_handler ( * this ) : nullptr ;
} else return nullptr ;
}
inline constexpr const std : : type_info * get_type_info ( ) const noexcept override final { return & typeid ( T ) ; }
T * data ;
HandleF handler ;
@ -279,6 +334,7 @@ constexpr inline opaque_handle make_opaque_handle(T* data) noexcept
{ return const_cast < T * > ( data ) ; }
constexpr opaque_handle_impl * try_clone ( ) const override final
{ return data ? new object_handler ( * this ) : nullptr ; }
inline constexpr const std : : type_info * get_type_info ( ) const noexcept override final { return & typeid ( T ) ; }
T * data ;
} ;