@ -20,6 +20,16 @@ struct opaque_handle_impl;
template < typename T >
template < typename T >
constexpr inline bool opaque_is_incomplete = ! requires { { sizeof ( T ) } ; } ;
constexpr inline bool opaque_is_incomplete = ! requires { { sizeof ( T ) } ; } ;
# define $impl _opaque__impl
namespace $ impl {
template < typename T >
concept CompleteType = ! opaque_is_incomplete < T > ;
}
/// A handler that always fails, only really useful for `make_opaque_handle`s that start with null pointers or ones that do not need to be freed and cannot be copied.
template < typename T >
constexpr inline auto opaque_empty_handler = [ ] [[gnu::const, gnu::always_inline]] ( auto * , auto ) noexcept - > T * { return nullptr ; } ;
struct opaque_handle_impl {
struct opaque_handle_impl {
friend class opaque_handle ;
friend class opaque_handle ;
@ -47,7 +57,7 @@ struct opaque_handle_impl {
template < typename T >
template < typename T >
constexpr bool is_type ( ) const noexcept {
constexpr bool is_type ( ) const noexcept {
if constexpr ( requires { { sizeof ( T ) } ; } ) return is_type ( typeid ( T ) ) ; else return true;
if constexpr ( requires { { sizeof ( T ) } ; } ) return is_type ( typeid ( T ) ) ; else return false; //XXX: Should this be `true`?
}
}
constexpr virtual bool is_type ( const std : : type_info & a ) const noexcept {
constexpr virtual bool is_type ( const std : : type_info & a ) const noexcept {
if ( const auto * p = get_type_info ( ) ) return * p = = a ;
if ( const auto * p = get_type_info ( ) ) return * p = = a ;
@ -98,9 +108,21 @@ struct opaque_handle final {
/// WARNING: try_cast<T>(), where T is an incomplete type, will always succeed, which may lead to undefined behaviour. Make sure to account for this
/// WARNING: try_cast<T>(), where T is an incomplete type, will always succeed, which may lead to undefined behaviour. Make sure to account for this
template < typename T >
template < typename T >
constexpr const T * try_cast ( ) const noexcept { return _impl ? _impl - > try_cast < T > ( ) : nullptr ; }
constexpr const T * try_cast ( ) const noexcept { static_assert ( ! opaque_is_incomplete < T > , " try_cast<T> always returns nullptr when T is an incomplete type, use `ptr_cast()` if T may be incomplete, or `unsafe_cast()` is it is known to be incomplete " ) ;
return _impl ? _impl - > try_cast < T > ( ) : nullptr ; }
template < typename T >
template < typename T >
constexpr T * try_cast ( ) noexcept { return _impl ? _impl - > try_cast < T > ( ) : nullptr ; }
constexpr T * try_cast ( ) noexcept { static_assert ( ! opaque_is_incomplete < T > , " try_cast<T> always returns nullptr when T is an incomplete type, use `ptr_cast()` if T may be incomplete, or `unsafe_cast()` if it is known to be incomplete. " ) ;
return _impl ? _impl - > try_cast < T > ( ) : nullptr ; }
template < $ impl : : CompleteType T >
constexpr bool is_type ( ) const noexcept {
return is_type ( typeid ( T ) ) ;
}
constexpr bool is_type ( const std : : type_info & ty ) const noexcept
{
return _impl ? _impl - > is_type ( ty ) : false ;
}
/// ptr_cast(): use try_cast() for complete types, and unsafe_cast() for incomplete types.
/// ptr_cast(): use try_cast() for complete types, and unsafe_cast() for incomplete types.
@ -115,14 +137,15 @@ struct opaque_handle final {
else return try_cast < T > ( ) ;
else return try_cast < T > ( ) ;
}
}
template < typenam e T , cast_kind Kind = CAST_SAFE >
template < $ impl : : CompleteTyp e T , cast_kind Kind = CAST_SAFE >
inline const T & cast ( ) const {
inline const T & cast ( ) const {
if constexpr ( Kind > = CAST_UNSAFE )
if constexpr ( Kind > = CAST_UNSAFE )
return * ( unsafe_cast < T , ( Kind = = CAST_UNSAFE_REINTERPRET ) > ( ) ? : throw opaque_bad_cast { } ) ;
return * ( unsafe_cast < T , ( Kind = = CAST_UNSAFE_REINTERPRET ) > ( ) ? : throw opaque_bad_cast { } ) ;
else
else { // CAST_SAFE
return * ( try_cast < T > ( ) ? : throw opaque_bad_cast { } ) ;
return * ( try_cast < T > ( ) ? : throw opaque_bad_cast { } ) ;
}
}
template < typename T , cast_kind Kind = CAST_SAFE >
}
template < $ impl : : CompleteType T , cast_kind Kind = CAST_SAFE >
inline T & cast ( ) {
inline T & cast ( ) {
if constexpr ( Kind > = CAST_UNSAFE )
if constexpr ( Kind > = CAST_UNSAFE )
return * ( unsafe_cast < T , ( Kind = = CAST_UNSAFE_REINTERPRET ) > ( ) ? : throw opaque_bad_cast { } ) ;
return * ( unsafe_cast < T , ( Kind = = CAST_UNSAFE_REINTERPRET ) > ( ) ? : throw opaque_bad_cast { } ) ;
@ -230,28 +253,32 @@ opaque_handle_object(T&&) -> opaque_handle_object<T>;
template < typename T >
template < typename T >
constexpr inline opaque_handle make_opaque_object_handle ( T & & value ) noexcept ( std : : is_nothrow_move_constructible_v < T > )
constexpr inline opaque_handle make_opaque_object_handle ( T & & value ) noexcept ( std : : is_nothrow_move_constructible_v < T > )
{
{
return opaque_handle { static_cast < opaque_handle_impl * > ( new opaque_handle_object < T > ( std : : forward < T > ( value ) ) ) } ;
return opaque_handle { static_cast < opaque_handle_impl * > ( new opaque_handle_object < T > ( std : : move ( value ) ) ) } ;
}
/// Make an opaque_handle from a newly constructed object `T`.
template < typename T , typename . . . Args > requires ( std : : is_constructible_v < T , Args . . . > )
constexpr inline opaque_handle make_opaque_object_handle ( Args & & . . . ctor ) noexcept ( std : : is_nothrow_constructible_v < T , Args . . . > )
{
return opaque_handle { static_cast < opaque_handle_impl * > ( new opaque_handle_object < T > ( std : : forward < Args > ( ctor ) . . . ) ) } ;
}
}
/// Operation for `OpaqueHandleFunc` functions.
enum class opaque_handle_operation
enum class opaque_handle_operation
{
{
Clone ,
Clone ,
Delete ,
Delete ,
} ;
} ;
/// A function that handles the de-allocation and cloning (if supported) of an opaque_handle created with `make_opaque_handle`.
template < typename F , typename T >
template < typename F , typename T >
concept OpaqueHandleFunc = std : : is_invocable_v < F , T * , opaque_handle_operation >
concept OpaqueHandleFunc = std : : is_invocable_v < F , T * , opaque_handle_operation >
& & std : : is_convertible_v < std : : invoke_result_t < F , T * , opaque_handle_operation > , T * > ;
& & std : : is_convertible_v < std : : invoke_result_t < F , T * , opaque_handle_operation > , T * > ;
//TODO: Turn all local `constexpr is_nothrow` in the following functions into #define macros. The constexpr locals being used in a local class cause the compiler to segfault for some reason...
//XXX: TODO: `make_opaque_handle()`'s local classes cannot be casted back to `T*`, adding operator T* won't help here because they are static/dynamic casted. We need them to inherit `opaque_handle_impl`
/// Create and opaque_handle from a data pointer and a lambda which handles the copying (if possible) and deleting of the object
/// Create and opaque_handle from a data pointer and a lambda which handles the copying (if possible) and deleting of the object
///
///
/// `handler` should be in the form: `auto* (T*, opaque_handle_operation) [noexcept]` and the reference must outlive the returned object. It should handle a `null` argument, and it can return `null`.
/// `handler` should be in the form: `auto* (T*, opaque_handle_operation) [noexcept]` and the reference must outlive the returned object. It should handle a `null` argument, and it can return `null`.
template < typename T , OpaqueHandleFunc < T > HandleF > // Note: This should be a lambda, or a free-function. it's lifetime must not end before the `opaque_handle` returned from this function does.
template < typename T , OpaqueHandleFunc < T > HandleF >
constexpr inline opaque_handle make_opaque_handle ( T * data , const HandleF & handler ) noexcept
constexpr inline opaque_handle make_opaque_handle ( T * data , const HandleF & handler ) noexcept
{
{
# define is_nothrow (std::is_nothrow_invocable_v<HandleF, T*, opaque_handle_operation>)
# define is_nothrow (std::is_nothrow_invocable_v<HandleF, T*, opaque_handle_operation>)
@ -270,7 +297,7 @@ constexpr inline opaque_handle make_opaque_handle(T* data, const HandleF& handle
constexpr virtual ~ object_handler ( ) //noexcept(is_nothrow)
constexpr virtual ~ object_handler ( ) //noexcept(is_nothrow)
{
{
if ( data ) handler ( data , opaque_handle_operation : : Delete ) ;
if ( data ) ( void ) handler ( data , opaque_handle_operation : : Delete ) ;
}
}
inline constexpr void * as_raw_ptr ( ) const noexcept override final
inline constexpr void * as_raw_ptr ( ) const noexcept override final
@ -316,7 +343,7 @@ constexpr inline opaque_handle make_opaque_handle(T* data, HandleF&& handler) no
constexpr virtual ~ object_handler ( ) //noexcept(is_nothrow)
constexpr virtual ~ object_handler ( ) //noexcept(is_nothrow)
{
{
if ( data ) handler ( data , opaque_handle_operation : : Delete ) ;
if ( data ) ( void ) handler ( data , opaque_handle_operation : : Delete ) ;
}
}
inline constexpr void * as_raw_ptr ( ) const noexcept override final
inline constexpr void * as_raw_ptr ( ) const noexcept override final
@ -364,7 +391,7 @@ constexpr inline opaque_handle make_opaque_handle(T* data) noexcept
constexpr virtual ~ object_handler ( ) //noexcept(is_nothrow)
constexpr virtual ~ object_handler ( ) //noexcept(is_nothrow)
{
{
if ( data ) Func ( data , opaque_handle_operation : : Delete ) ;
if ( data ) ( void ) Func ( data , opaque_handle_operation : : Delete ) ;
}
}
inline constexpr void * as_raw_ptr ( ) const noexcept override final
inline constexpr void * as_raw_ptr ( ) const noexcept override final
@ -386,3 +413,4 @@ constexpr inline opaque_handle make_opaque_handle(T* data) noexcept
# undef $_oe_msg
# undef $_oe_msg
# undef $_oe
# undef $_oe
# undef $impl