diff --git a/include/opaque.hh b/include/opaque.hh index fbee0da..9792efc 100644 --- a/include/opaque.hh +++ b/include/opaque.hh @@ -13,6 +13,7 @@ struct opaque_exception : std::exception $_oe_msg("unspecified opaque_handle rel 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_unknown_type : $_oe("handle refers to an unknown or incomplete type"); struct opaque_handle; struct opaque_handle_impl; @@ -124,6 +125,14 @@ struct opaque_handle final { return _impl ? _impl->is_type(ty) : false; } + constexpr const std::type_info* try_get_type() const noexcept { + return _impl ? _impl->get_type_info() : nullptr; + } + + constexpr const std::type_info& get_type() const { + if(__builtin_expect(_impl == nullptr, false)) throw opaque_handle_moved{}; + return *(_impl->get_type_info() ?: throw opaque_unknown_type{}); + } /// ptr_cast(): use try_cast() for complete types, and unsafe_cast() for incomplete types. template diff --git a/src/main.cpp b/src/main.cpp index 5b8d97d..628a611 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,18 +5,101 @@ #include +#include + #include "../include/opaque.hh" -static void s_puts(const auto& string) +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) + { + const auto _free = [&]() { free(static_cast(str)); }; + + if constexpr(std::is_nothrow_constructible_v) { + 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(1, str.data(), str.size()); + write(fileno(to), str.data(), str.size()); #else - printf("%.*s", (int)str.size(), str.data()); + 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(" "); + 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}; @@ -55,6 +138,10 @@ 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: \n"); } + print(*v); { auto copied = v; @@ -119,6 +206,15 @@ static void use_c_struct() opaque_handle impossible{make_opaque_handle>(nullptr)}; black_box(impossible); + s_puts("Testing .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(allocp, [](auto* ptr, auto op) noexcept { if(ptr) { switch(op) { @@ -129,6 +225,10 @@ static void use_c_struct() return static_cast(nullptr); })}; + try { + print_name(v.get_type()); + } catch(const opaque_unknown_type&) { s_puts("type holds: \n"); } + s_puts("> "); cs_print(v); { @@ -144,8 +244,28 @@ int main() { const opaque_handle v{make_opaque_object_handle("Hello world")}; + + { + using prim_t = int; + struct never_t{}; + auto prim = make_opaque_object_handle(-115); + puts("Primitive type handling"); + s_puts("(pre-mutation)"); + { + prim_t& vref = prim.cast(); + print(std::to_string(std::as_const(vref))); + vref *= -1; + } + s_puts("(post-mutation)"); + print(std::to_string(std::as_const(prim).cast())); + print_name(prim.get_type()); + s_puts(prim.is_type() ? "\t-> +v: yes, " : "\t-> +v: no, "); + s_puts(prim.is_type() ? "!v: yes\n" : "!v: no\n"); + } + + puts("\nAuto-downcasting to `const std::string&`:"); + print_name(v.get_type()); - puts("Auto-downcasting to `const std::string&`:"); print(*v); #if 1 s_puts("\nAttempting invalid safe cast... ");