# pragma once
# 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 {
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 { } ;
struct opaque_handle ;
struct opaque_handle_impl ;
struct opaque_handle_impl {
friend class opaque_handle ;
virtual constexpr ~ opaque_handle_impl ( ) noexcept { }
template < typename To >
inline constexpr const To * try_cast ( ) const noexcept { return static_cast < const To * > ( as_raw_ptr ( ) ) ; }
template < typename To >
inline constexpr To * try_cast ( ) noexcept { return static_cast < To * > ( as_raw_ptr ( ) ) ; }
protected :
constexpr virtual void * as_raw_ptr ( ) const noexcept = 0 ;
constexpr virtual opaque_handle_impl * try_clone ( ) const = 0 ;
inline virtual opaque_handle_impl & clone ( ) const {
if ( auto * np = try_clone ( ) ) return * np ;
throw opaque_not_copyable { } ;
}
constexpr opaque_handle_impl ( ) noexcept { }
} ;
struct opaque_handle final {
constexpr explicit opaque_handle ( opaque_handle_impl * ptr ) noexcept : _impl ( ptr ) { }
constexpr opaque_handle ( opaque_handle & & move ) noexcept
: _impl ( std : : exchange ( move . _impl , nullptr ) ) { }
inline opaque_handle ( const opaque_handle & copy )
: _impl ( copy . _impl
? std : : addressof ( copy . _impl - > clone ( ) ) //(copy._impl->try_clone() ?: throw opaque_not_copyable{})
: nullptr ) { }
constexpr ~ opaque_handle ( )
{
if ( _impl ) delete _impl ;
}
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 >
constexpr const T * try_cast ( ) const noexcept { return static_cast < const T * > ( as_ptr ( ) ) ; }
template < typename T >
constexpr T * try_cast ( ) noexcept { return static_cast < T * > ( as_ptr ( ) ) ; }
constexpr bool has_value ( ) const noexcept { return bool ( _impl ) ; }
constexpr explicit operator bool ( ) const noexcept { return has_value ( ) ; }
template < typename T >
constexpr const T * operator - > ( ) const noexcept { return try_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`.
inline auto operator * ( ) const noexcept {
return _impl_deref_const { * this } ;
}
inline auto operator * ( ) noexcept {
return _impl_deref { * this } ;
}
/*
template < typename T >
inline auto operator * ( ) noexcept { return * ( try_cast < T > ( ) ? : throw opaque_bad_cast { } ) ; }
*/
template < typename T >
constexpr operator T * ( ) noexcept { return try_cast < T > ( ) ; }
template < typename T >
constexpr operator const T * ( ) const noexcept { return try_cast < T > ( ) ; }
private :
struct _impl_deref_const {
const opaque_handle & ptr ;
template < typename T >
inline operator const T & ( ) & & {
return * ( 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 { } ) ;
}
} ;
struct _impl_deref {
opaque_handle & ptr ;
template < typename T >
inline operator T & ( ) & & {
return * ( 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 { } ) ;
}
} ;
opaque_handle_impl * _impl ;
} ;
template < typename T >
struct opaque_handle_object : public opaque_handle_impl
{
friend class opaque_handle ;
inline constexpr opaque_handle_object ( T & & value ) noexcept ( std : : is_nothrow_move_constructible_v < T > )
: _obj ( std : : move ( value ) ) { }
inline constexpr virtual ~ opaque_handle_object ( ) { }
protected :
inline constexpr void * as_raw_ptr ( ) const noexcept override { return const_cast < void * > ( static_cast < const void * > ( & _obj ) /*?: (static_cast<void*>(&_obj) ?: &_obj)*/ ) ; }
inline constexpr opaque_handle_impl * try_clone ( ) const override {
if constexpr ( std : : is_copy_constructible_v < T > ) {
return new opaque_handle_object < T > { T { _obj } } ;
} else return nullptr ;
}
private :
T _obj ;
} ;
template < typename T >
opaque_handle_object ( T & & ) - > opaque_handle_object < T > ;
/// Make an opaque_handle from an object.
template < typename 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 ) ) ) } ;
}
enum class opaque_handle_operation
{
Clone ,
Delete ,
} ;
template < typename F , typename T >
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 * > ;
/// 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 > ;
struct object_handler
{
constexpr object_handler ( const object_handler & c ) noexcept ( is_nothrow )
: data ( c . handler ( data , opaque_handle_operation : : Clone ) )
, handler ( c . handler ) { }
constexpr object_handler ( object_handler & & m ) noexcept
: data ( std : : exchange ( m . data , nullptr ) )
, handler ( m . handler ) { }
constexpr object_handler ( T * data , const HandleF & handler ) noexcept
: data ( data )
, handler ( handler ) { }
constexpr /*virtual*/ ~ object_handler ( ) noexcept ( is_nothrow )
{
if ( data ) handler ( data , opaque_handle_operation : : Delete ) ;
}
T * data ;
const HandleF & handler ;
} ;
return make_opaque_object_handle ( object_handler { data , handler } ) ;
}
/// Create and opaque_handle from a data pointer and a move-constructible functor which handles the copying (if possible) and deleting of the object.
///
/// `handler` should be in the form: `auto* (T*, opaque_handle_operation) [noexcept]`. Its lifetime is managed by the returned opaque_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 const bool is_nothrow = std : : is_nothrow_invocable_v < HandleF , T * , opaque_handle_operation > ;
constexpr const bool is_nothrow_ctor = std : : is_nothrow_move_constructible_v < HandleF > ;
struct object_handler
{
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 ) )
, handler ( c . handler ) { }
constexpr object_handler ( object_handler & & m ) noexcept
: data ( std : : exchange ( m . data , nullptr ) )
, handler ( std : : move ( m . handler ) ) { }
constexpr object_handler ( T * data , HandleF & & handler ) noexcept ( is_nothrow_ctor )
: data ( data )
, handler ( std : : move ( handler ) ) { }
constexpr /*virtual*/ ~ object_handler ( ) noexcept ( is_nothrow )
{
if ( data ) handler ( data , opaque_handle_operation : : Delete ) ;
}
T * data ;
HandleF handler ;
} ;
return make_opaque_object_handle ( object_handler { data , std : : forward < HandleF > ( handler ) } ) ;
}
/// Create and opaque_handle from a data pointer and a constexpr handler funcion template param (e.g. a lambda)
///
/// `Func` should be in the form: `auto* (T*, opaque_handle_operation) [noexcept]`. It must be a constant expression.
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 > ;
struct object_handler
{
constexpr object_handler ( const object_handler & c ) noexcept ( is_nothrow )
: data ( Func ( data , opaque_handle_operation : : Clone ) ) { }
constexpr object_handler ( object_handler & & m ) noexcept
: data ( std : : exchange ( m . data , nullptr ) ) { }
constexpr object_handler ( T * data ) noexcept
: data ( data ) { }
constexpr /*virtual*/ ~ object_handler ( ) noexcept ( is_nothrow )
{
if ( data ) Func ( data , opaque_handle_operation : : Delete ) ;
}
T * data ;
} ;
return make_opaque_object_handle ( object_handler { data } ) ;
}