/* file Reflect.hpp * * author: Roland Conybeare, Aug 2022 */ #pragma once #include "SelfTagging.hpp" #include "EstablishTypeDescr.hpp" #include "atomic/AtomicTdx.hpp" #include "pointer/PointerTdx.hpp" #include "vector/VectorTdx.hpp" #include "struct/StructTdx.hpp" #include "function/FunctionTdx.hpp" #include "xo/refcnt/Refcounted.hpp" #include #include #include // for std::pair<> namespace xo { namespace reflect { 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(); } }; // ----- xo::ref::rp ----- template class EstablishTdx> { public: /* definition provide after decl for Reflect {} below */ static std::unique_ptr make(); }; // ----- std::array ----- template class EstablishTdx> { public: /* definition provide after decl for Reflect {} below */ static std::unique_ptr make(); }; // ----- std::vector ----- template class EstablishTdx> { public: /* definition provide after decl for Reflect {} below */ static std::unique_ptr make(); }; // ----- std::pair ----- template class EstablishTdx> { public: /* definition provide after decl for Reflect {} below */ static std::unique_ptr make(); }; // ----- Retval (*)(A1 .. An) ----- template class EstablishTdx { public: /* definition provided after decl for Reflect {} below */ static std::unique_ptr make(); }; // ----- Retval (*)(A1 .. An) noexcept ----- template class EstablishTdx { public: /* definition provided after decl for Reflect {} below */ static std::unique_ptr make(); }; // ----- EstasblishFunctionTdx ------------------------------------- template class EstablishFunctionTdx; // ----- Retval (*)(A1 .. An) ----- template class EstablishFunctionTdx { public: /* definition provided after decl for Reflect {} below */ static std::unique_ptr make(); }; template class EstablishFunctionTdx { public: /* definition provided after decl for Reflect {} below */ static std::unique_ptr make(); }; // ----- MakeTagged ----- template class TaggedPtrMaker { public: static TaggedPtr make_tp(T * x); static TaggedRcptr make_rctp(T * x); }; template<> class TaggedPtrMaker { public: static TaggedPtr make_tp(SelfTagging * x) { return x->self_tp(); } /*make_tp*/ static TaggedRcptr make_rctp(SelfTagging * x) { return x->self_tp(); } /*make_rctp*/ }; /*TaggedPtrMaker*/ // ----- Reflect ----- class Reflect { public: /* Use: * using mytype = ...; * if (Reflect::is_reflected()) { ... } */ template static bool is_reflected() { return TypeDescrBase::is_reflected(&typeid(T)); } /* Use: * using mytype = ...; * TypeDescrW td = Reflect::require(); * * Note: * To avoid cyclic header dependencies * (between EstablishTypeDescr.hpp <-> {vector/VectorTdx.hpp etc.}, * we use a 2-stage setup process: * * 1. EstablishTypeDescr::establish() creates a TypeDescr* object * with lowest-common-denominator .tdextra AtomicTdx. * (see [reflect/EstablishTypeDescr.hpp]) * * 2. Reflect::require() upgrades .tdextra to suitable implementation * depending on T; this means also need to visit reflection info * (TypeDescr objects) for nested types to upgrade them too. * * This allows template-fu for a compound type (like std::vector), * implemented in specialized header (like [reflect/struct/VectorTdx.hpp]) to * refer to reflection info for T without having to pull in all the * headers needed to properly reflect T (like this [reflect/Reflect.hpp]) * */ template static TypeDescrW require() { TypeDescrW retval_td = EstablishTypeDescr::establish(); /* mark TypeDescr for T as complete (even though it isn't quite yet), * so that when we encounter recursive types, reflection terminates. * For example consider type resulting from code like * * typename T; * using T = std::vector; * */ if (retval_td->mark_complete()) { /* control here on 2nd+later calls to require(). * in principle can immediately short-circuit. */ } else { /* control comes here the first time require() runs */ auto final_tdx = EstablishTdx::make(); retval_td->assign_tdextra(Reflect::get_final_invoker(), std::move(final_tdx)); /* also need to require for each child */ } return retval_td; } /*require*/ /* can optionally use this when reflecting a function pointer. * Should get the same result as reflect(), * but will not fallback to AtomicTdx if T is not recognized as a function pointer */ template static TypeDescrW require_function() { //static_assert(std::is_function_v); TypeDescrW retval_td = EstablishTypeDescr::establish(); /* mark TypeDescr for T as complete (even though it isn't quite yet), * so that when we encounter recursive types, reflection terminates. * For example consider type resulting from code like * * typename T; * using T = std::vector; * */ if (retval_td->mark_complete()) { /* control here on 2nd+later calls to require(). * in principle can immediately short-circuit. */ } else { /* control comes here the first time require() runs */ auto final_tdx = EstablishFunctionTdx::make(); retval_td->assign_tdextra(Reflect::get_final_invoker(), std::move(final_tdx)); /* also need to require for each child */ } return retval_td; } /** true iff @p src_td is a type-description for @tparam T **/ template static bool is_native(TypeDescr src_td) { return (require() == src_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); */ template static TaggedPtr make_tp(T * x) { return TaggedPtrMaker::make_tp(x); } template static TaggedRcptr make_rctp(T * x) { return TaggedPtrMaker::make_rctp(x); } template static detail::InvokerAux * get_final_invoker() { static detail::InvokerAux s_final_invoker; return &s_final_invoker; } }; /*Reflect*/ // ----- MakeTagged ----- template TaggedPtr TaggedPtrMaker::make_tp(T * x) { return TaggedPtr(Reflect::require(), x); } /*make_tp*/ template TaggedRcptr TaggedPtrMaker::make_rctp(T * x) { return TaggedRcptr(Reflect::require(), x); } /*make_rctp*/ // ----- xo::ref::rp ----- /* declared above before * class Reflect { .. } */ template std::unique_ptr EstablishTdx>::make() { /* need to ensure Object is property reflected. * * In practice must be a class type, since has to store refcount * + supply assoc'd incr/decr methods */ Reflect::require(); return RefPointerTdx>::make(); } /*make*/ // ----- std::array ----- /* declared above before * class Reflect { .. } */ template std::unique_ptr EstablishTdx>::make() { /* need to ensure Element is properly reflected */ Reflect::require(); return StdArrayTdx::make(); } /*make*/ // ----- std::vector ----- /* declared above before * class Reflect { .. } */ template std::unique_ptr EstablishTdx>::make() { /* need to ensure Element is properly reflected */ Reflect::require(); return StdVectorTdx::make(); } /*make*/ // ----- std::pair ----- /* declared above before * class Reflect { .. } */ template std::unique_ptr EstablishTdx>::make() { /* need to ensure Lhs, Rhs are properly reflected */ Reflect::require(); Reflect::require(); 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*/ template std::unique_ptr EstablishFunctionTdx::make() { std::vector argv; detail::AssembleArgv::append_argv(&argv); return FunctionTdx::make_function(Reflect::require(), std::move(argv), false /*!is_noexcept*/); } template std::unique_ptr EstablishFunctionTdx::make() { std::vector argv; detail::AssembleArgv::append_argv(&argv); return FunctionTdx::make_function(Reflect::require(), std::move(argv), true /*is_noexcept*/); } /* 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), false /*!is_noexcept*/); } /* 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), true /*is_noexcept*/); } } /*namespace reflect*/ } /*namespace xo*/ /* end Reflect.hpp */