//#define _GNU_SOURCE
# include <cstdlib>
# include <cstring>
# include <cstdio>
# include <unistd.h>
# include <cxxabi.h>
# include "../include/opaque.hh"
struct demangle_failed : std : : exception {
virtual int status ( ) const noexcept = 0 ;
const char * what ( ) const noexcept override {
auto s = status ( ) ;
switch ( s ) {
case - 1 : return " A memory allocation failure occurred " ;
case - 2 : return " `mangled` was not a valid name under the C++ ABI mangling rules " ;
case - 3 : return " One of the arguments was invalid " ;
default : return " Unknown error " ;
}
}
protected :
inline demangle_failed ( ) : std : : exception ( ) { }
} ;
static std : : string demangle_name ( const char * mangled )
{
struct demangle_error final : public demangle_failed
{
inline demangle_error ( int s ) : demangle_failed ( ) , _status ( s ) { }
inline int status ( ) const noexcept override final { return _status ; }
int _status ;
} ;
size_t len ;
int status ;
char * demangled = abi : : __cxa_demangle ( mangled , nullptr , & len , & status ) ;
const auto c_str_to_string = [ len ] ( char * str )
noexcept ( std : : is_nothrow_constructible_v < std : : string , const char * , size_t > )
{
const auto _free = [ & ] ( ) { free ( static_cast < void * > ( str ) ) ; } ;
if constexpr ( std : : is_nothrow_constructible_v < std : : string , const char * , size_t > ) {
std : : string v { str , len } ;
_free ( ) ;
return v ;
} else {
try {
std : : string v { str , len } ;
_free ( ) ;
return v ;
} catch ( . . . ) {
_free ( ) ;
throw ;
}
}
} ;
if ( demangled )
return c_str_to_string ( demangled ) ;
else
throw demangle_error ( status ) ;
}
inline static std : : string demangle_name ( const std : : string & mangled )
{
return demangle_name ( mangled . c_str ( ) ) ;
}
static inline void s_puts ( const auto & string , FILE * to = nullptr )
{
std : : string_view str { string } ;
to = to ? : stdout ;
# ifdef DIRECT_WRITE
write ( fileno ( to ) , str . data ( ) , str . size ( ) ) ;
# else
fprintf ( to , " %.*s " , ( int ) str . size ( ) , str . data ( ) ) ;
# endif
}
static void print_name ( const std : : type_info & type )
{
const char * name = type . name ( ) ;
s_puts ( " type holds: " ) ;
try {
s_puts ( demangle_name ( name ) ) ;
s_puts ( " \n " ) ;
} catch ( demangle_failed & err ) {
s_puts ( " <mangled> " ) ;
s_puts ( name ) ;
s_puts ( " \n " ) ;
s_puts ( " Failed to demangle name: " , stderr ) ;
s_puts ( err . what ( ) , stderr ) ;
s_puts ( " \n " , stderr ) ;
}
}
static void print_view ( std : : string_view ptr )
{
//std::string_view ptr{string};
size_t sz ;
# ifdef DIRECT_WRITE
// Bypass all caching of the output and immediately invoke `write()` for everything
if ( __builtin_expect ( ( sz = ptr . size ( ) ) > 0 , true ) ) {
write ( 1 , " > " , 2 ) ;
write ( 1 , ptr . data ( ) , sz ) ;
} else write ( 1 , " ! <null> \n " , 8 ) ;
write ( 1 , " \n " , 1 ) ;
# else
// All the output from the program is cached and `write()` is only called once at the end.
if ( __builtin_expect ( ( sz = ptr . size ( ) ) > 0 , true ) ) {
printf ( " > %.*s \n " , ( int ) sz , ptr . data ( ) ) ;
} else puts ( " ! <null> \n " ) ;
# endif
}
static inline void print ( const std : : string & string )
{
std : : string_view sv { string } ;
return print_view ( sv ) ;
}
enum class moh_hck {
Template ,
Ref ,
Owned ,
} ;
/// Example using `make_opaque_handle`, like would be done for a C interface
template < moh_hck Create = moh_hck : : Owned >
static void use_moh ( )
{
std : : string * str = new std : : string ( __PRETTY_FUNCTION__ ) ;
constexpr auto v_print = [ ] ( const opaque_handle & v ) {
try {
print_name ( v . get_type ( ) ) ;
} catch ( const opaque_unknown_type & ) { s_puts ( " type holds: <unknown> \n " ) ; }
print ( * v ) ;
{
auto copied = v ;
s_puts ( " (copied) " ) ;
print ( * copied ) ;
s_puts ( " (moved) " ) ;
print ( * std : : move ( copied ) ) ;
}
} ;
if constexpr ( Create ! = moh_hck : : Owned ) {
constexpr auto _h = [ ] ( std : : string * ptr , auto op ) noexcept {
if ( ptr ) {
switch ( op ) {
case opaque_handle_operation : : Clone : return new std : : string ( std : : as_const ( * ptr ) ) ;
case opaque_handle_operation : : Delete : delete ptr ; break ;
}
}
return static_cast < std : : string * > ( nullptr ) ;
} ;
if constexpr ( Create = = moh_hck : : Template ) {
const opaque_handle v { make_opaque_handle < std : : string , _h > ( str ) } ;
v_print ( v ) ;
} else /*if constexpr(Create == moh_hck::Ref)*/ {
const opaque_handle v { make_opaque_handle < std : : string > ( str , _h ) } ;
v_print ( v ) ;
}
} else {
const opaque_handle v { make_opaque_handle < std : : string > ( str , [ ] ( std : : string * ptr , auto op ) noexcept {
if ( ptr ) {
switch ( op ) {
case opaque_handle_operation : : Clone : return new std : : string ( std : : as_const ( * ptr ) ) ;
case opaque_handle_operation : : Delete : delete ptr ; break ;
}
}
return static_cast < std : : string * > ( nullptr ) ;
} ) } ;
v_print ( v ) ;
}
s_puts ( " --- \n " ) ;
}
// Testing a hypothetical C interface
extern " C " {
struct c_struct_example ;
struct opaque ;
void black_box ( const opaque * __restrict__ ) { }
c_struct_example * cs_create ( const c_struct_example * __restrict__ from ) ;
void cs_free ( c_struct_example * p ) ;
void cs_print ( const c_struct_example * p ) ;
}
static void use_c_struct ( )
{
using type = c_struct_example ;
auto * allocp = cs_create ( nullptr ) ;
opaque_handle impossible { make_opaque_handle < opaque , opaque_empty_handler < opaque > > ( nullptr ) } ;
black_box ( impossible ) ;
s_puts ( " Testing <incomplete>.get_type() throwing... " ) ;
try {
print_name ( impossible . get_type ( ) ) ;
s_puts ( " FAILED (expected `opaque_unknown_type` exception) \n " ) ;
std : : terminate ( ) ;
} catch ( const opaque_unknown_type & ) {
s_puts ( " OK \n " ) ;
}
const opaque_handle v { make_opaque_handle < type > ( allocp , [ ] ( auto * ptr , auto op ) noexcept {
if ( ptr ) {
switch ( op ) {
case opaque_handle_operation : : Clone : return cs_create ( ptr ) ;
case opaque_handle_operation : : Delete : cs_free ( ptr ) ; break ;
}
}
return static_cast < type * > ( nullptr ) ;
} ) } ;
try {
print_name ( v . get_type ( ) ) ;
} catch ( const opaque_unknown_type & ) { s_puts ( " type holds: <unknown at this point> \n " ) ; }
s_puts ( " > " ) ;
cs_print ( v ) ;
{
auto copied = v ;
s_puts ( " (copied)> " ) ;
cs_print ( copied ) ;
s_puts ( " (moved)> " ) ;
cs_print ( std : : move ( copied ) ) ;
}
}
int main ( )
{
const
opaque_handle v { make_opaque_object_handle < std : : string > ( " Hello world " ) } ;
{
using prim_t = int ;
struct never_t { } ;
auto prim = make_opaque_object_handle < prim_t > ( - 115 ) ;
puts ( " Primitive type handling " ) ;
s_puts ( " (pre-mutation) " ) ;
{
prim_t & vref = prim . cast < prim_t > ( ) ;
print ( std : : to_string ( std : : as_const ( vref ) ) ) ;
vref * = - 1 ;
}
s_puts ( " (post-mutation) " ) ;
print ( std : : to_string ( std : : as_const ( prim ) . cast < prim_t > ( ) ) ) ;
print_name ( prim . get_type ( ) ) ;
s_puts ( prim . is_type < prim_t > ( ) ? " \t -> +v: yes, " : " \t -> +v: no, " ) ;
s_puts ( prim . is_type < never_t > ( ) ? " !v: yes \n " : " !v: no \n " ) ;
}
puts ( " \n Auto-downcasting to `const std::string&`: " ) ;
print_name ( v . get_type ( ) ) ;
print ( * v ) ;
# if 1
s_puts ( " \n Attempting invalid safe cast... " ) ;
try {
print_view ( * v ) ; // Try to cast to std::string_view, not the correct type...
s_puts ( " FAILED \n " ) ;
return 1 ;
} catch ( opaque_bad_cast & ) { // Expected this exception to be thrown
s_puts ( " OK \n " ) ;
} catch ( opaque_exception & other ) {
s_puts ( " UNEXPECTED ( " ) ;
s_puts ( other . what ( ) ) ;
s_puts ( " ) \n " ) ;
return 2 ;
}
# endif
puts ( " \n Converting view directly: " ) ;
print_view ( std : : string_view { v . cast < std : : string , opaque_handle : : CAST_UNSAFE /* SAFETY: We know this is the type of `v`'s pointee, so we don't need the type check. */ > ( ) } ) ; // Explicit cast needed, *v will choose the wrong type if not.
puts ( " \n Using opaque_make_handle(): " ) ;
use_moh < moh_hck : : Template > ( ) ;
use_moh < moh_hck : : Ref > ( ) ;
use_moh < moh_hck : : Owned > ( ) ;
puts ( " \n Using a C interface struct: " ) ;
use_c_struct ( ) ;
return 0 ;
}
extern " C " {
struct c_struct_example {
int i , j ;
size_t sz ;
char buf [ 256 ] ;
} ;
c_struct_example * cs_create ( const c_struct_example * __restrict__ from )
{
auto * ptr = reinterpret_cast < c_struct_example * > ( aligned_alloc ( alignof ( c_struct_example ) , sizeof ( c_struct_example ) ) ) ;
//memset(ptr, 0, sizeof(c_struct_example);
if ( ! from ) {
ptr - > i = 10 ;
ptr - > j = 11 ;
snprintf ( ptr - > buf , 256 , " Hello from C interface " ) ;
} else {
//memcpy(ptr, from, sizeof(c_struct_example));
* ptr = * from ;
ptr - > i + = 1 ;
ptr - > j - = 1 ;
snprintf ( ptr - > buf , 256 , " Hello from copied C interface (from %p) " , from ) ;
}
return ptr ;
}
void cs_print ( const c_struct_example * p )
{
# ifdef DIRECT_WRITE
char ibuf [ 512 ] ;
size_t n = snprintf ( ibuf , 512 , " %s: (%d, %d) " , p - > buf , p - > i , p - > j ) ;
write ( 1 , ibuf , n < sizeof ( ibuf ) ? n : 511 ) ;
write ( 1 , " \n " , 1 ) ;
# else
printf ( " %s: (%d, %d) \n " , p - > buf , p - > i , p - > j ) ;
# endif
}
void cs_free ( c_struct_example * p )
{
free ( p ) ;
}
}