@ -3,63 +3,13 @@
# include <utility>
# include <stdexcept>
#if 0 //XXX: This doesn't work, we need `opaque_handle` to not be a template...
namespace _opaque_impl {
struct opaque_handle {
# 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 " ) ;
virtual constexpr ~ opaque_handle ( ) ;
protected :
inline constexpr explicit opaque_handle ( void * data ) noexcept : _ptr ( data ) { }
template < typename From >
inline constexpr static opaque_handle from_object ( T & ptr ) noexcept
{
return opaque_handle { dynamic_cast < void * > ( & ptr ) ? : static_cast < void * > ( & ptr ) } ;
}
inline constexpr void * ptr ( ) const noexcept {
return _ptr ;
}
template < typename To >
inline constexpr T * downcast ( ) const noexcept {
return dynamic_cast < To * > ( _ptr ) ;
}
template < typename To >
inline constexpr downcast_unsafe ( ) const noexcept {
return static_cast < To * > ( _ptr ) ;
}
template < typename To >
inline T * ptr_as ( ) { return std : : bit_cast < To > ( _ptr ) ; }
private :
void * _ptr ;
} ;
template < typename T >
struct opaque_handle_for : opaque_handle
{
constexpr opaque_handle_for ( T & & value )
: opaque_handle ( opaque_handle : : template from_object < T > ( * new T ( std : : move ( value ) ) ) ) { }
constexpr virtual ~ opaque_handle_for ( )
{
if ( T * ptr = downcast_unsafe < T > ( ) ) {
delete ptr ;
}
}
}
}
struct opaque_handle
{
private :
} ;
# endif
struct opaque_not_copyable : std : : exception { } ;
struct opaque_handle_moved : std : : exception { } ;
struct opaque_bad_cast : std : : exception { } ;
# define $_oe(msg) public opaque_exception $_oe_msg((msg))
struct opaque_not_copyable : $ _oe ( " opaque_handle referred to a type that was not copyable " ) ;
struct opaque_handle_moved : $ _oe ( " this opaque_handle was moved " ) ;
struct opaque_bad_cast : $ _oe ( " bad opaque_handle pointer cast " ) ;
struct opaque_handle ;
struct opaque_handle_impl ;
@ -67,7 +17,7 @@ struct opaque_handle_impl;
struct opaque_handle_impl {
friend class opaque_handle ;
virtual constexpr ~ opaque_handle_impl ( ) noexcept { }
virtual constexpr ~ opaque_handle_impl ( ) { }
template < typename To >
inline constexpr const To * try_cast ( ) const noexcept { return static_cast < const To * > ( as_raw_ptr ( ) ) ; }
@ -105,11 +55,20 @@ 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 ; }
///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 >
inline const T & cast ( ) const { return * ( try_cast < T > ( ) ? : throw opaque_bad_cast { } ) ; }
template < typename T >
inline T & cast ( ) { 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 ( ) ; }
@ -205,15 +164,17 @@ concept OpaqueHandleFunc = std::is_invocable_v<F, T*, opaque_handle_operation>
//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
///
/// `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.
constexpr inline opaque_handle make_opaque_handle ( T * data , const HandleF & handler ) noexcept
{
constexpr const bool 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>)
struct object_handler
struct object_handler final : opaque_handle_impl
{
constexpr object_handler ( const object_handler & c ) noexcept ( is_nothrow )
: data ( c . handler ( data , opaque_handle_operation : : Clone ) )
@ -225,16 +186,22 @@ constexpr inline opaque_handle make_opaque_handle(T* data, const HandleF& handle
: data ( data )
, handler ( handler ) { }
constexpr /*virtual*/ ~ object_handler ( ) noexcept ( is_nothrow )
constexpr virtual ~ object_handler ( ) //noexcept(is_nothrow)
{
if ( data ) handler ( data , opaque_handle_operation : : Delete ) ;
}
inline constexpr void * as_raw_ptr ( ) const noexcept override final
{ return const_cast < T * > ( data ) ; }
constexpr opaque_handle_impl * try_clone ( ) const override final
{ return data ? new object_handler ( * this ) : nullptr ; }
T * data ;
const HandleF & handler ;
} ;
return make_opaque_object_handle ( object_handler { data , handler } ) ;
return opaque_handle ( static_cast < opaque_handle_impl * > ( new object_handler ( data , handler ) ) ) ;
# undef is_nothrow
}
/// Create and opaque_handle from a data pointer and a move-constructible functor which handles the copying (if possible) and deleting of the object.
@ -243,10 +210,10 @@ constexpr inline opaque_handle make_opaque_handle(T* data, const HandleF& handle
template < typename T , OpaqueHandleFunc < T > HandleF >
constexpr inline opaque_handle make_opaque_handle ( T * data , HandleF & & handler ) noexcept ( std : : is_nothrow_move_constructible_v < HandleF > )
{
constexpr bool is_nothrow = std : : is_nothrow_invocable_v < HandleF , T * , opaque_handle_operation > ; //XXX: Causes segfault... TODO: turn these into a #define macro
constexpr bool is_nothrow_ctor = std : : is_nothrow_move_constructible_v < HandleF > ;
# define is_nothrow (std::is_nothrow_invocable_v<HandleF, T*, opaque_handle_operation>)
# define is_nothrow_ctor (std::is_nothrow_move_constructible_v<HandleF>)
struct object_handler
struct object_handler final : opaque_handle_impl
{
constexpr object_handler ( const object_handler & c ) noexcept ( is_nothrow & & std : : is_nothrow_copy_constructible_v < HandleF > ) requires ( std : : is_copy_constructible_v < HandleF > )
: data ( c . handler ( data , opaque_handle_operation : : Clone ) )
@ -259,16 +226,27 @@ constexpr inline opaque_handle make_opaque_handle(T* data, HandleF&& handler) no
: data ( data )
, handler ( std : : move ( handler ) ) { }
constexpr /*virtual*/ ~ object_handler ( ) noexcept ( is_nothrow )
constexpr virtual ~ object_handler ( ) //noexcept(is_nothrow)
{
if ( data ) handler ( data , opaque_handle_operation : : Delete ) ;
}
inline constexpr void * as_raw_ptr ( ) const noexcept override final
{ return const_cast < T * > ( data ) ; }
constexpr opaque_handle_impl * try_clone ( ) const override final
{
if constexpr ( std : : is_copy_constructible_v < HandleF > ) {
return data ? new object_handler ( * this ) : nullptr ;
} else return nullptr ;
}
T * data ;
HandleF handler ;
} ;
return make_opaque_object_handle ( object_handler { data , std : : forward < HandleF > ( handler ) } ) ;
return opaque_handle ( static_cast < opaque_handle_impl * > ( new object_handler ( data , std : : forward < HandleF > ( handler ) ) ) ) ;
# undef is_nothrow
# undef is_nothrow_ctor
}
/// Create and opaque_handle from a data pointer and a constexpr handler funcion template param (e.g. a lambda)
@ -278,12 +256,12 @@ template<typename T, OpaqueHandleFunc<T> auto Func>
constexpr inline opaque_handle make_opaque_handle ( T * data ) noexcept
{
using HandleF = decltype ( Func ) ;
constexpr bool 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>)
struct object_handler
struct object_handler final : opaque_handle_impl
{
constexpr object_handler ( const object_handler & c ) noexcept ( is_nothrow )
: data ( Func ( data, opaque_handle_operation : : Clone ) ) { }
: data ( Func ( c. data, opaque_handle_operation : : Clone ) ) { }
constexpr object_handler ( object_handler & & m ) noexcept
: data ( std : : exchange ( m . data , nullptr ) ) { }
@ -291,13 +269,22 @@ constexpr inline opaque_handle make_opaque_handle(T* data) noexcept
constexpr object_handler ( T * data ) noexcept
: data ( data ) { }
constexpr /*virtual*/ ~ object_handler ( ) noexcept ( is_nothrow )
constexpr virtual ~ object_handler ( ) //noexcept(is_nothrow)
{
if ( data ) Func ( data , opaque_handle_operation : : Delete ) ;
}
inline constexpr void * as_raw_ptr ( ) const noexcept override final
{ return const_cast < T * > ( data ) ; }
constexpr opaque_handle_impl * try_clone ( ) const override final
{ return data ? new object_handler ( * this ) : nullptr ; }
T * data ;
} ;
return make_opaque_object_handle ( object_handler { data } ) ;
return opaque_handle ( static_cast < opaque_handle_impl * > ( new object_handler ( data ) ) ) ;
# undef is_nothrow
}
# undef $_oe_msg
# undef $_oe