From ef191b136ebf5cee087ee19855cbfbbf4460253a Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 14 Jun 2024 10:56:35 -0400 Subject: [PATCH] xo-reflect: automatically reflecct function pointers --- include/xo/reflect/EstablishTypeDescr.hpp | 89 ++++++++++-------- include/xo/reflect/Reflect.hpp | 84 +++++++++++++++-- include/xo/reflect/TaggedPtr.hpp | 12 ++- include/xo/reflect/TypeDescr.hpp | 11 +++ include/xo/reflect/TypeDescrExtra.hpp | 108 ++++++++++++---------- 5 files changed, 206 insertions(+), 98 deletions(-) diff --git a/include/xo/reflect/EstablishTypeDescr.hpp b/include/xo/reflect/EstablishTypeDescr.hpp index 32e546d..13c8099 100644 --- a/include/xo/reflect/EstablishTypeDescr.hpp +++ b/include/xo/reflect/EstablishTypeDescr.hpp @@ -9,53 +9,66 @@ #include "TaggedPtr.hpp" namespace xo { - namespace reflect { - class EstablishTypeDescr { - public: - /* implementation method; expect this to be used only within reflect/ library. - * avoids some otherwise-cyclic #include paths - * between specialized headers such as vector/VectorTdx.hpp and this - * EstablishTypeDescr.hpp - */ + namespace reflect { + /** @class EstablishTypeDescr + * @brief class to establish globally-unique TypeDescr object for a type T + * + * We don't require the full definition of T to use EstablishTypeDescr::establish(). + * In particular, a forward declaration is sufficient. + * + * Additional information (that depends on full definition) may be attached later, + * by assigning (once-only) to @ref TypeDescr::tdextra_ + * + * @note Application code will use @ref Reflect::require; that in turn relies on the + * template @ref EstablishTdx to leverage template pattern-matching for + * recurring patterns. + **/ + class EstablishTypeDescr { + public: + /* implementation method; expect this to be used only within reflect/ library. + * avoids some otherwise-cyclic #include paths + * between specialized headers such as vector/VectorTdx.hpp and this + * EstablishTypeDescr.hpp + */ #ifdef OBSOLETE - template - static TaggedPtr establish_tp(T * x) { return TaggedPtr(establish(), x); } + template + static TaggedPtr establish_tp(T * x) { return TaggedPtr(establish(), x); } #endif - template - static TaggedPtr establish_most_derived_tp(T * x) { return establish()->most_derived_self_tp(x); } + template + static TaggedPtr establish_most_derived_tp(T * x) { return establish()->most_derived_self_tp(x); } - template - static TypeDescrW establish() { - TypeDescrW td = TypeDescrBase::require(&typeid(T), - type_name(), - nullptr); + template + static TypeDescrW establish() { + TypeDescrW td = TypeDescrBase::require(&typeid(T), + type_name(), + nullptr); #ifdef NOT_USING - std::function to_self_tp; + std::function to_self_tp; - if (std::is_base_of_v) { - /* T is a descendant of SelfTagging (or T = SelfTagging); - * use SelfTagging.self_tp() - */ - to_self_tp = [](void * x) { return reinterpret_cast(x)->self_tp(); }; - } else { - /* T is not a descendant of SelfTagging. - * want to return - */ - to_self_tp = [td](void * x) { return TaggedPtr(td, x); }; - } + if (std::is_base_of_v) { + /* T is a descendant of SelfTagging (or T = SelfTagging); + * use SelfTagging.self_tp() + */ + to_self_tp = [](void * x) { return reinterpret_cast(x)->self_tp(); }; + } else { + /* T is not a descendant of SelfTagging. + * want to return + */ + to_self_tp = [td](void * x) { return TaggedPtr(td, x); }; + } - td->assign_to_self_tp(to_self_tp); + td->assign_to_self_tp(to_self_tp); #endif - return td; - } - }; /*EstablishTypeDescr*/ + return td; + } + }; /*EstablishTypeDescr*/ - template - inline TaggedPtr establish_most_derived_tp(T * x) { - return EstablishTypeDescr::establish_most_derived_tp(x); - } - } /*namespace reflect*/ + template + inline TaggedPtr establish_most_derived_tp(T * x) { + return EstablishTypeDescr::establish_most_derived_tp(x); + } + } /*namespace reflect*/ } /*namespace xo*/ diff --git a/include/xo/reflect/Reflect.hpp b/include/xo/reflect/Reflect.hpp index 89a7537..bba62ae 100644 --- a/include/xo/reflect/Reflect.hpp +++ b/include/xo/reflect/Reflect.hpp @@ -11,6 +11,7 @@ #include "pointer/PointerTdx.hpp" #include "vector/VectorTdx.hpp" #include "struct/StructTdx.hpp" +#include "function/FunctionTdx.hpp" #include "xo/refcnt/Refcounted.hpp" #include #include @@ -21,44 +22,69 @@ namespace xo { template class EstablishTdx { public: + /** Create auxiliary reflection info for type @tparam T, + * once full definition is available. + * + * This includes: + * - metatype + * - component structure (types for navigable component objects) + * + **/ static std::unique_ptr make() { return AtomicTdx::make(); } - }; /*EstablishTdx*/ + }; // ----- xo::ref::rp ----- - /* definition provide after decl for Reflect {} below */ template class EstablishTdx> { public: + /* definition provide after decl for Reflect {} below */ static std::unique_ptr make(); - }; /*EstablishTdx*/ + }; // ----- std::array ----- - /* definition provide after decl for Reflect {} below */ template class EstablishTdx> { public: + /* definition provide after decl for Reflect {} below */ static std::unique_ptr make(); - }; /*EstablishTdx*/ + }; // ----- std::vector ----- - /* definition provide after decl for Reflect {} below */ template class EstablishTdx> { public: + /* definition provide after decl for Reflect {} below */ static std::unique_ptr make(); - }; /*EstablishTdx*/ + }; // ----- std::pair ----- - /* definition provide after decl for Reflect {} below */ template class EstablishTdx> { public: + /* definition provide after decl for Reflect {} below */ static std::unique_ptr make(); - }; /*EstablishTdx*/ + }; + + // ----- Retval (*)(A1 .. An) ----- + + template + class EstablishTdx { + public: + /* definition provided after decl for Reflect {} below */ + static std::unique_ptr make(); + }; + + // ----- Retval (*)() ----- + + template + class EstablishTdx { + /* definition provided after decl for Reflect {} below */ + static std::unique_ptr make(); + }; // ----- MakeTagged ----- @@ -229,6 +255,46 @@ namespace xo { return StructTdx::pair(); } /*make*/ + + // ----- Retval (*) (A1 .. An) ----- + + namespace detail { + /** @class AssembleArgv + * @brief create vector of complete TypeDescr objects comprising all template arguments + * + * Use: + * std::vector v; + * AssembleArgv::append_argv(&v); + * // do something with v + **/ + template + struct AssembleArgv; + + template <> + struct AssembleArgv<> { + static void append_argv(std::vector * p_v) {} + }; + + template + struct AssembleArgv { + static void append_argv(std::vector * p_v) { + p_v->push_back(Reflect::require()); + AssembleArgv::append_argv(p_v); + } + }; + } /*detail*/ + + /* declared above before + * class Reflect { ... } + */ + template + std::unique_ptr + EstablishTdx::make() { + std::vector argv; + detail::AssembleArgv::append_argv(&argv); + + return FunctionTdx::make_function(Reflect::require(), std::move(argv)); + } } /*namespace reflect*/ } /*namespace xo*/ diff --git a/include/xo/reflect/TaggedPtr.hpp b/include/xo/reflect/TaggedPtr.hpp index 7bb520a..dce3fa0 100644 --- a/include/xo/reflect/TaggedPtr.hpp +++ b/include/xo/reflect/TaggedPtr.hpp @@ -59,9 +59,10 @@ namespace xo { void assign_address(void * x) { address_ = x; } bool is_universal_null() const { return (td_ == nullptr) && (address_ == nullptr); } + bool is_pointer() const { return td_ && td_->is_pointer(); } bool is_vector() const { return td_ && td_->is_vector(); } bool is_struct() const { return td_ && td_->is_struct(); } - + bool is_function() const { return td_ && td_->is_function(); } /* returns pointer-to-T, if in fact this tagged pointer is understood * to refer to a T-instance; otherwise nullptr @@ -71,11 +72,16 @@ namespace xo { uint32_t n_child() const { return this->td_->n_child(this->address_); - } /*n_child*/ + } TaggedPtr get_child(uint32_t i) const { return this->td_->child_tp(i, this->address_); - } /*get_child*/ + } + + /* if reflected function (.is_function() = true): + * number of arguments to that function + */ + uint32_t n_fn_arg() const { return this->td_->n_fn_arg(); } /* require: * - .is_struct() is true diff --git a/include/xo/reflect/TypeDescr.hpp b/include/xo/reflect/TypeDescr.hpp index 94aa3aa..c50a6d8 100644 --- a/include/xo/reflect/TypeDescr.hpp +++ b/include/xo/reflect/TypeDescr.hpp @@ -179,8 +179,10 @@ namespace xo { } } /*recover_native*/ + 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(); } + bool is_function() const { return this->tdextra_->is_function(); } /* given a T-instance object, return tagged pointer with T replaced * by the most-derived-subtype of T to which *object belongs. @@ -203,6 +205,7 @@ namespace xo { std::string const & struct_member_name(uint32_t i) const { return this->tdextra_->struct_member_name(i); } + /* fetch runtime description for i'th reflected instance variable. * * require: @@ -216,6 +219,14 @@ namespace xo { return *sm; } /*struct_member*/ + uint32_t n_fn_arg() const { return this->tdextra_->n_fn_arg(); } + + /* require: + * - .is_function() = true + */ + TypeDescr fn_retval() const { return this->tdextra_->fn_retval(); } + TypeDescr fn_arg(uint32_t i) const { return this->tdextra_->fn_arg(i); } + void display(std::ostream & os) const; std::string display_string() const; diff --git a/include/xo/reflect/TypeDescrExtra.hpp b/include/xo/reflect/TypeDescrExtra.hpp index 824cd5d..54db302 100644 --- a/include/xo/reflect/TypeDescrExtra.hpp +++ b/include/xo/reflect/TypeDescrExtra.hpp @@ -8,58 +8,70 @@ #include namespace xo { - namespace reflect { - /* forward-declaring here. see [reflect/struct/StructMember.hpp] */ - class StructMember; - class TypeDescrBase; - class TaggedPtr; + namespace reflect { + /* forward-declaring here. see [reflect/struct/StructMember.hpp] */ + class StructMember; + class TypeDescrBase; + class TaggedPtr; - /* information associated with a c++ type. - * distinct from TypeDescrImpl: - * 1. want to use reflection to support for runtime polymorphism over similar but - * not directly-related types: for example - * std::vector - * and - * std::list - * are both ordered collections - * 2. some information can't be universally established via template-fu, - * for example struct member names - * 3. descriptions for recursive types require 2-stage construction - * - * A TypeDescrImpl instance will contain a pointer to a suitable - * TypeDescrExtra instance. - * - * The single TypeDescrImpl instance for some type T can be established - * automatically, see Reflect::require(). - * - * A specific TypeDescrExtra instance may be attached in a non-automated way - * later - */ - class TypeDescrExtra { - public: - using uint32_t = std::uint32_t; + /* information associated with a c++ type. + * distinct from TypeDescrImpl: + * 1. want to use reflection to support for runtime polymorphism over similar but + * not directly-related types: for example + * std::vector + * and + * std::list + * are both ordered collections + * 2. some information can't be universally established via template-fu, + * for example struct member names + * 3. descriptions for recursive types require 2-stage construction + * + * A TypeDescrImpl instance will contain a pointer to a suitable + * TypeDescrExtra instance. + * + * The single TypeDescrImpl instance for some type T can be established + * automatically, see Reflect::require(). + * + * A specific TypeDescrExtra instance may be attached in a non-automated way + * later + */ + class TypeDescrExtra { + public: + using uint32_t = std::uint32_t; - public: - virtual ~TypeDescrExtra() = default; + public: + virtual ~TypeDescrExtra() = default; - bool is_vector() const { return this->metatype() == Metatype::mt_vector; } - bool is_struct() const { return this->metatype() == Metatype::mt_struct; } + bool is_pointer() const { return this->metatype() == Metatype::mt_pointer; } + bool is_vector() const { return this->metatype() == Metatype::mt_vector; } + bool is_struct() const { return this->metatype() == Metatype::mt_struct; } + bool is_function() const { return this->metatype() == Metatype::mt_function; } - virtual Metatype metatype() const = 0; - /* given a T-instance, report most-derived subtype of T to which *object belongs. - * this works only for types that are derived from reflect::SelfTagging. - */ - virtual TaggedPtr most_derived_self_tp(TypeDescrBase const * object_td, void * object) const; - virtual uint32_t n_child(void * object) const = 0; - virtual TaggedPtr child_tp(uint32_t i, void * object) const = 0; - /* require: - * .is_struct() - */ - virtual std::string const & struct_member_name(uint32_t i) const = 0; - /* nullptr unless *this represents a struct/class type */ - virtual StructMember const * struct_member(uint32_t i) const; - }; /*TypeDescrExtra*/ - } /*namespace reflect*/ + virtual Metatype metatype() const = 0; + /* given a T-instance, report most-derived subtype of T to which *object belongs. + * this works only for types that are derived from reflect::SelfTagging. + */ + virtual TaggedPtr most_derived_self_tp(TypeDescrBase const * object_td, void * object) const; + virtual uint32_t n_child(void * object) const = 0; + virtual TaggedPtr child_tp(uint32_t i, void * object) const = 0; + /* require: + * .is_struct() + */ + virtual std::string const & struct_member_name(uint32_t i) const = 0; + /* nullptr unless *this represents a struct/class type */ + virtual StructMember const * struct_member(uint32_t i) const; + + // methods for working with reflected functions/methods + + /** number of arguments to function-like value + * + * @pre @ref TypeDescrExtra::is_function() is true + **/ + virtual uint32_t n_fn_arg() const { return 0; } + virtual const TypeDescrBase * fn_retval() const { return nullptr; } + virtual const TypeDescrBase * fn_arg(uint32_t /*i_arg*/) const { return nullptr; } + }; /*TypeDescrExtra*/ + } /*namespace reflect*/ } /*namespace xo*/ /* end TypeDescrExtra.hpp */