diff --git a/README.md b/README.md index 528d10e0..370cbafb 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ or equivalently $ git clone git@github.com:Rconybea/xo-reflect.git ``` -### build + install xo-reflect +### build + install `xo-reflect` ``` $ xo-build --configure --build --install xo-reflect ``` diff --git a/include/xo/reflect/Reflect.hpp b/include/xo/reflect/Reflect.hpp index 20e862c9..e10519a8 100644 --- a/include/xo/reflect/Reflect.hpp +++ b/include/xo/reflect/Reflect.hpp @@ -227,6 +227,15 @@ namespace xo { return retval_td; } + /** given address @p src_address of a value with type described by @p src, + * return typed pointer of type @tparam T, provided that @p src_td + * actually describes @tparam T. Otherwise returns nullptr + **/ + template + static T * recover_native(TypeDescr src_td, void * src_address) { + return TaggedPtr(src_td, src_address).recover_native(); + } + /* Use: * T * xyz = ...; * TaggedPtr xyz_tp = Reflect::make_tp(xyz); diff --git a/include/xo/reflect/TaggedPtr.hpp b/include/xo/reflect/TaggedPtr.hpp index dce3fa06..fbffaa04 100644 --- a/include/xo/reflect/TaggedPtr.hpp +++ b/include/xo/reflect/TaggedPtr.hpp @@ -68,7 +68,7 @@ namespace xo { * to refer to a T-instance; otherwise nullptr */ template - T * recover_native() const { return this->td_->recover_native(this->address_); } + T * recover_native() const { return this->td_->recover_native2(this->td_, this->address_); } uint32_t n_child() const { return this->td_->n_child(this->address_); diff --git a/include/xo/reflect/TypeDescr.hpp b/include/xo/reflect/TypeDescr.hpp index 54013654..4d49876b 100644 --- a/include/xo/reflect/TypeDescr.hpp +++ b/include/xo/reflect/TypeDescr.hpp @@ -149,28 +149,66 @@ namespace xo { static void print_reflected_types(std::ostream & os); TypeId id() const { return id_; } - std::type_info const * typeinfo() const { return typeinfo_; } + std::type_info const * native_typeinfo() const { return native_typeinfo_; } std::string_view const & canonical_name() const { return canonical_name_; } std::string_view const & short_name() const { return short_name_; } bool complete_flag() const { return complete_flag_; } TypeDescrExtra * tdextra() const { return tdextra_.get(); } Metatype metatype() const { return tdextra_->metatype(); } - /* true iff the type represented by *this is the same as the type T. + /* true iff the type represented by *this is the same as the type + * represented by T. * * Warning: comparing typeinfo address can give false negatives. * suspect this is caused by problems coalescing linker symbols * in the clang toolchain. */ template + [[deprecated]] bool is_native() const { - return ((this->typeinfo() == &typeid(T)) - || (this->typeinfo()->hash_code() == typeid(T).hash_code()) - || (this->typeinfo()->name() == typeid(T).name())); + if (this->native_typeinfo()) { + /* reminder: typeid(T).name() is 'interesting' but not intended + * to be human-readable. It's not how compiler labels + * a type for a human reader + */ + return ((this->native_typeinfo() == &typeid(T)) + || (this->native_typeinfo()->hash_code() == typeid(T).hash_code()) + || (this->native_typeinfo()->name() == typeid(T).name())); + } else { + /** if this type was established via Reflect::require(), + * then .canonical_name is computed by type_name() + * + * (see demangle.hh in xo-refcnt, which post-processes __PRETTY_FUNCTION__ + * or __FUNCSIG__) + * + * To manually construct an equivalent type, + * it's necessary to: + * 1. construct a unique and unambiguous canonical name for the type + * 2. be aware that type will only be recognized as equivalent to + * a natively-reflected type if canonical name matches exactly. + **/ + + /** FOR NOW: give up. **/ + throw std::runtime_error("TypeDescrBase::is_native: not implemented for manually-constructed TypeDescr objects. Prefer is_native2()"); + } } /*is_native*/ - /* safe downcast -- like dynamic_cast<>, but does not require a source type */ + /** safe downcast -- like dynamic_cast<>, but does not require a source type. + * + * TODO: need variation on this to correctly-handle function types, + * since for exampple cast from void* -> void (*)() is not allowed + * + * WARNING: relies on deprecated is_native(). Application code should prefer any of: + * 1. recover_native2(src_td, src_address) + * 2. Reflect::recover_native(src_td, src_address) + * 3. TaggedPtr(src_td,src_address).recover_native() + * instead of src_td->recover_native() + * + * (note: awkwardness here is that we don't have access to {Reflect.hpp, TaggedPtr.hpp} + * from this .hpp file, since TypeDescr.hpp is included by those headers) + **/ template + [[deprecated]] T * recover_native(void * address) const { if (this->is_native()) { return reinterpret_cast(address); @@ -179,6 +217,22 @@ namespace xo { } } /*recover_native*/ + /** safe downcast -- like dynamic_cast<>, but does not require a source type. + * + * Application code should prefer TaggedPtr::recover_native() + * + * TODO: need variation on this to correctly-handle function types, + * since for exampple cast from void* -> void (*)() is not allowed + **/ + template + T * recover_native2(TypeDescr address_td, void * address) const { + if (this == address_td) { + return reinterpret_cast(address); + } else { + return nullptr; + } + } /*recover_native2*/ + bool is_pointer() const { return this->tdextra_->is_pointer(); } bool is_vector() const { return this->tdextra_->is_vector(); } bool is_struct() const { return this->tdextra_->is_struct(); } @@ -255,7 +309,9 @@ namespace xo { * - s_type_table_map[TypeInfoRef(x->typeinfo())] = x */ - /* hashmap of all TypeDescr instances, indexed by . singleton */ + /* hashmap of all native TypeDescr instances, indexed by typeinfo. + * singleton. + */ static std::unordered_map> s_type_table_map; /* hashmap of (presumed) duplicate TypeInfoRef values. * This happens with clang sometimes when the same type is referenced @@ -269,8 +325,13 @@ namespace xo { private: /* unique id# for this type */ TypeId id_; - /* typeinfo for type T */ - std::type_info const * typeinfo_ = nullptr; + /** typeinfo for type T, if available. nullptr otherwise. + * + * 1. Always available for type-descriptions constructed via Reflect::require. + * 2. Always missing for manually-constructed TypeDescr instances, for example + * see Lambda.cpp in xo-expression. + **/ + std::type_info const * native_typeinfo_ = nullptr; /* canonical name for this type (see demangle.hpp for type_name()) * e.g. * xo::option::Px2 diff --git a/src/reflect/TypeDescr.cpp b/src/reflect/TypeDescr.cpp index b0a0dfd7..8eaea8e0 100644 --- a/src/reflect/TypeDescr.cpp +++ b/src/reflect/TypeDescr.cpp @@ -124,11 +124,11 @@ namespace xo { } /*namespace*/ TypeDescrBase::TypeDescrBase(TypeId id, - std::type_info const * tinfo, + std::type_info const * native_tinfo, std::string_view canonical_name, std::unique_ptr tdextra) : id_{std::move(id)}, - typeinfo_{tinfo}, + native_typeinfo_{native_tinfo}, canonical_name_{std::move(canonical_name)}, short_name_{unqualified_name(canonical_name_)}, tdextra_{std::move(tdextra)}