Added `opaque_handle::[try_]get_type()`: Get an `std::type_info` if possible.

Added typing related tests to main.

Fortune for opaque_handle's current commit: Small blessing − 小吉
master
Avril 3 years ago
parent 95e6cae2ce
commit 8dea5823a5
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -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_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_handle_moved : $_oe("this opaque_handle was moved");
struct opaque_bad_cast : $_oe("bad opaque_handle pointer cast"); 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;
struct opaque_handle_impl; struct opaque_handle_impl;
@ -124,6 +125,14 @@ struct opaque_handle final {
return _impl ? _impl->is_type(ty) : false; 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. /// ptr_cast(): use try_cast() for complete types, and unsafe_cast() for incomplete types.
template<typename T> template<typename T>

@ -5,18 +5,101 @@
#include <unistd.h> #include <unistd.h>
#include <cxxabi.h>
#include "../include/opaque.hh" #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<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}; std::string_view str{string};
to = to ?: stdout;
#ifdef DIRECT_WRITE #ifdef DIRECT_WRITE
write(1, str.data(), str.size()); write(fileno(to), str.data(), str.size());
#else #else
printf("%.*s", (int)str.size(), str.data()); fprintf(to, "%.*s", (int)str.size(), str.data());
#endif #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) static void print_view(std::string_view ptr)
{ {
//std::string_view ptr{string}; //std::string_view ptr{string};
@ -55,6 +138,10 @@ static void use_moh()
std::string* str = new std::string(__PRETTY_FUNCTION__); std::string* str = new std::string(__PRETTY_FUNCTION__);
constexpr auto v_print = [](const opaque_handle& v) { 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); print(*v);
{ {
auto copied = v; auto copied = v;
@ -119,6 +206,15 @@ static void use_c_struct()
opaque_handle impossible{make_opaque_handle<opaque, opaque_empty_handler<opaque>>(nullptr)}; opaque_handle impossible{make_opaque_handle<opaque, opaque_empty_handler<opaque>>(nullptr)};
black_box(impossible); 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 { const opaque_handle v{make_opaque_handle<type>(allocp, [](auto* ptr, auto op) noexcept {
if(ptr) { if(ptr) {
switch(op) { switch(op) {
@ -129,6 +225,10 @@ static void use_c_struct()
return static_cast<type*>(nullptr); 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("> "); s_puts("> ");
cs_print(v); cs_print(v);
{ {
@ -145,7 +245,27 @@ int main()
const const
opaque_handle v{make_opaque_object_handle<std::string>("Hello world")}; opaque_handle v{make_opaque_object_handle<std::string>("Hello world")};
puts("Auto-downcasting to `const std::string&`:"); {
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("\nAuto-downcasting to `const std::string&`:");
print_name(v.get_type());
print(*v); print(*v);
#if 1 #if 1
s_puts("\nAttempting invalid safe cast... "); s_puts("\nAttempting invalid safe cast... ");

Loading…
Cancel
Save