xo-reflect: automatically reflecct function pointers

This commit is contained in:
Roland Conybeare 2024-06-14 10:56:35 -04:00
commit ef191b136e
5 changed files with 209 additions and 101 deletions

View file

@ -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<T>().
* 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<typename T>
static TaggedPtr establish_tp(T * x) { return TaggedPtr(establish<T>(), x); }
template<typename T>
static TaggedPtr establish_tp(T * x) { return TaggedPtr(establish<T>(), x); }
#endif
template<typename T>
static TaggedPtr establish_most_derived_tp(T * x) { return establish<T>()->most_derived_self_tp(x); }
template<typename T>
static TaggedPtr establish_most_derived_tp(T * x) { return establish<T>()->most_derived_self_tp(x); }
template<typename T>
static TypeDescrW establish() {
TypeDescrW td = TypeDescrBase::require(&typeid(T),
type_name<T>(),
nullptr);
template<typename T>
static TypeDescrW establish() {
TypeDescrW td = TypeDescrBase::require(&typeid(T),
type_name<T>(),
nullptr);
#ifdef NOT_USING
std::function<TaggedPtr (void *)> to_self_tp;
std::function<TaggedPtr (void *)> to_self_tp;
if (std::is_base_of_v<SelfTagging, T>) {
/* T is a descendant of SelfTagging (or T = SelfTagging);
* use SelfTagging.self_tp()
*/
to_self_tp = [](void * x) { return reinterpret_cast<T *>(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<SelfTagging, T>) {
/* T is a descendant of SelfTagging (or T = SelfTagging);
* use SelfTagging.self_tp()
*/
to_self_tp = [](void * x) { return reinterpret_cast<T *>(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<typename T>
inline TaggedPtr establish_most_derived_tp(T * x) {
return EstablishTypeDescr::establish_most_derived_tp<T>(x);
}
} /*namespace reflect*/
template<typename T>
inline TaggedPtr establish_most_derived_tp(T * x) {
return EstablishTypeDescr::establish_most_derived_tp<T>(x);
}
} /*namespace reflect*/
} /*namespace xo*/

View file

@ -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 <vector>
#include <array>
@ -21,44 +22,69 @@ namespace xo {
template<typename T>
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<TypeDescrExtra> make() { return AtomicTdx::make(); }
}; /*EstablishTdx*/
};
// ----- xo::ref::rp<Object> -----
/* definition provide after decl for Reflect {} below */
template<typename Object>
class EstablishTdx<xo::ref::rp<Object>> {
public:
/* definition provide after decl for Reflect {} below */
static std::unique_ptr<TypeDescrExtra> make();
}; /*EstablishTdx*/
};
// ----- std::array<Element, N> -----
/* definition provide after decl for Reflect {} below */
template<typename Element, std::size_t N>
class EstablishTdx<std::array<Element, N>> {
public:
/* definition provide after decl for Reflect {} below */
static std::unique_ptr<TypeDescrExtra> make();
}; /*EstablishTdx*/
};
// ----- std::vector<Element> -----
/* definition provide after decl for Reflect {} below */
template<typename Element>
class EstablishTdx<std::vector<Element>> {
public:
/* definition provide after decl for Reflect {} below */
static std::unique_ptr<TypeDescrExtra> make();
}; /*EstablishTdx*/
};
// ----- std::pair<Lhs, Rhs> -----
/* definition provide after decl for Reflect {} below */
template<typename Lhs, typename Rhs>
class EstablishTdx<std::pair<Lhs, Rhs>> {
public:
/* definition provide after decl for Reflect {} below */
static std::unique_ptr<TypeDescrExtra> make();
}; /*EstablishTdx*/
};
// ----- Retval (*)(A1 .. An) -----
template<typename Retval, typename... Args>
class EstablishTdx<Retval(*)(Args...)> {
public:
/* definition provided after decl for Reflect {} below */
static std::unique_ptr<TypeDescrExtra> make();
};
// ----- Retval (*)() -----
template <typename Retval>
class EstablishTdx<Retval(*)()> {
/* definition provided after decl for Reflect {} below */
static std::unique_ptr<TypeDescrExtra> make();
};
// ----- MakeTagged -----
@ -229,6 +255,46 @@ namespace xo {
return StructTdx::pair<Lhs, Rhs>();
} /*make*/
// ----- Retval (*) (A1 .. An) -----
namespace detail {
/** @class AssembleArgv
* @brief create vector of complete TypeDescr objects comprising all template arguments
*
* Use:
* std::vector<TypeDescr> v;
* AssembleArgv<Arg1, .., Argn>::append_argv(&v);
* // do something with v
**/
template <typename... Args>
struct AssembleArgv;
template <>
struct AssembleArgv<> {
static void append_argv(std::vector<TypeDescr> * p_v) {}
};
template <typename Arg, typename... Rest>
struct AssembleArgv<Arg, Rest...> {
static void append_argv(std::vector<TypeDescr> * p_v) {
p_v->push_back(Reflect::require<Arg>());
AssembleArgv<Rest...>::append_argv(p_v);
}
};
} /*detail*/
/* declared above before
* class Reflect { ... }
*/
template <typename Retval, typename... Args>
std::unique_ptr<TypeDescrExtra>
EstablishTdx<Retval (*)(Args...)>::make() {
std::vector<TypeDescr> argv;
detail::AssembleArgv<Args...>::append_argv(&argv);
return FunctionTdx::make_function(Reflect::require<Retval>(), std::move(argv));
}
} /*namespace reflect*/
} /*namespace xo*/

View file

@ -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

View file

@ -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;

View file

@ -8,58 +8,70 @@
#include <cstdint>
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<int>
* and
* std::list<std::string>
* 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<int>
* and
* std::list<std::string>
* 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 */