reflect: insert xo/ subdir into include path

This commit is contained in:
Roland Conybeare 2023-10-06 16:53:47 -04:00
commit 6be9037f10
20 changed files with 179 additions and 184 deletions

View file

@ -0,0 +1,32 @@
# reflect/CMakeLists.txt
set(SELF_LIBRARY_NAME reflect)
# build shared library 'reflect'
add_library(${SELF_LIBRARY_NAME} SHARED TypeDescr.cpp TypeDescrExtra.cpp TaggedRcptr.cpp atomic/AtomicTdx.cpp pointer/PointerTdx.cpp vector/VectorTdx.cpp struct/StructTdx.cpp struct/StructMember.cpp init_reflect.cpp)
set_target_properties(${SELF_LIBRARY_NAME} PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION 1
PUBLIC_HEADER TypeDescr.hpp)
# ----------------------------------------------------------------
# all the errors+warnings!
#
#target_compile_options(${SELF_LIBRARY_NAME} PRIVATE -Werror -Wall -Wextra)
xo_compile_options(${SELF_LIBRARY_NAME})
xo_include_options(${SELF_LIBRARY_NAME})
# ----------------------------------------------------------------
# internal dependencies: logutil, ...
target_link_libraries(${SELF_LIBRARY_NAME} PUBLIC refcnt)
# ----------------------------------------------------------------
# 3rd party dependency: boost:
#xo_boost_dependency(${SELF_LIBRARY_NAME})
xo_install_library(${SELF_LIBRARY_NAME})
# end CMakeLists.txt

View file

@ -0,0 +1,62 @@
/* file EstablishTypeDescr.hpp
*
* author: Roland Conybeare, Aug 2022
*/
#pragma once
#include "TypeDescr.hpp"
#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
*/
#ifdef OBSOLETE
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 TypeDescrW establish() {
TypeDescrW td = TypeDescrBase::require(&typeid(T),
type_name<T>(),
nullptr);
#ifdef NOT_USING
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); };
}
td->assign_to_self_tp(to_self_tp);
#endif
return td;
}
}; /*EstablishTypeDescr*/
template<typename T>
inline TaggedPtr establish_most_derived_tp(T * x) {
return EstablishTypeDescr::establish_most_derived_tp<T>(x);
}
} /*namespace reflect*/
} /*namespace xo*/
/* end EstablishTypeDescr.hpp */

View file

@ -0,0 +1,38 @@
/* @file Metatype.hpp */
#pragma once
#include <iostream>
namespace xo {
namespace reflect {
enum class Metatype { mt_invalid, mt_atomic, mt_pointer, mt_vector, mt_struct };
inline std::ostream & operator<<(std::ostream & os,
Metatype x) {
switch(x) {
case Metatype::mt_invalid:
os << "invalid!";
break;
case Metatype::mt_atomic:
os << "atomic";
break;
case Metatype::mt_pointer:
os << "pointer";
break;
case Metatype::mt_vector:
os << "vector";
break;
case Metatype::mt_struct:
os << "struct";
break;
default:
os << "???";
}
return os;
} /*operator<<*/
} /*namespace reflect*/
} /*namespace xo*/
/* end Metatype.hpp */

View file

@ -0,0 +1,235 @@
/* 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 "refcnt/Refcounted.hpp"
#include <vector>
#include <array>
#include <utility> // for std::pair<>
namespace xo {
namespace reflect {
template<typename T>
class EstablishTdx {
public:
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:
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:
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:
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:
static std::unique_ptr<TypeDescrExtra> make();
}; /*EstablishTdx*/
// ----- MakeTagged -----
template<typename T>
class TaggedPtrMaker {
public:
static TaggedPtr make_tp(T * x);
static TaggedRcptr make_rctp(T * x);
};
template<>
class TaggedPtrMaker<SelfTagging> {
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<mytype>()) { ... }
*/
template<typename T>
static bool is_reflected() { return TypeDescrBase::is_reflected(&typeid(T)); }
/* Use:
* using mytype = ...;
* TypeDescrW td = Reflect::require<mytype>();
*
* Note:
* To avoid cyclic header dependencies
* (between EstablishTypeDescr.hpp <-> {vector/VectorTdx.hpp etc.},
* we use a 2-stage setup process:
*
* 1. EstablishTypeDescr::establish<T>() creates a TypeDescr* object
* with lowest-common-denominator .tdextra AtomicTdx.
* (see [reflect/EstablishTypeDescr.hpp])
*
* 2. Reflect::require<T>() 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<T>),
* 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<typename T>
static TypeDescrW require() {
TypeDescrW retval_td = EstablishTypeDescr::establish<T>();
/* 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<T *>;
*
*/
if (retval_td->mark_complete()) {
/* control here on 2nd+later calls to require<T>().
* in principle can immediately short-circuit.
*/
} else {
/* control comes here the first time require<T>() runs */
auto final_tdx = EstablishTdx<T>::make();
retval_td->assign_tdextra(std::move(final_tdx));
/* also need to require for each child */
}
return retval_td;
} /*require*/
/* Use:
* T * xyz = ...;
* TaggedPtr xyz_tp = Reflect::make_tp(xyz);
*/
template<typename T>
static TaggedPtr make_tp(T * x) { return TaggedPtrMaker<T>::make_tp(x); }
template<typename T>
static TaggedRcptr make_rctp(T * x) { return TaggedPtrMaker<T>::make_rctp(x); }
}; /*Reflect*/
// ----- MakeTagged -----
template<typename T>
TaggedPtr
TaggedPtrMaker<T>::make_tp(T * x) {
return TaggedPtr(Reflect::require<T>(), x);
} /*make_tp*/
template<typename T>
TaggedRcptr
TaggedPtrMaker<T>::make_rctp(T * x) {
return TaggedRcptr(Reflect::require<T>(), x);
} /*make_rctp*/
// ----- xo::ref::rp<Object> -----
/* declared above before
* class Reflect { .. }
*/
template<typename Object>
std::unique_ptr<TypeDescrExtra>
EstablishTdx<xo::ref::rp<Object>>::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<Object>();
return RefPointerTdx<xo::ref::rp<Object>>::make();
} /*make*/
// ----- std::array<Element, N> -----
/* declared above before
* class Reflect { .. }
*/
template<typename Element, std::size_t N>
std::unique_ptr<TypeDescrExtra>
EstablishTdx<std::array<Element, N>>::make() {
/* need to ensure Element is properly reflected */
Reflect::require<Element>();
return StdArrayTdx<Element, N>::make();
} /*make*/
// ----- std::vector<Element> -----
/* declared above before
* class Reflect { .. }
*/
template<typename Element>
std::unique_ptr<TypeDescrExtra>
EstablishTdx<std::vector<Element>>::make() {
/* need to ensure Element is properly reflected */
Reflect::require<Element>();
return StdVectorTdx<Element>::make();
} /*make*/
// ----- std::pair<Lhs, Rhs> -----
/* declared above before
* class Reflect { .. }
*/
template<typename Lhs, typename Rhs>
std::unique_ptr<TypeDescrExtra>
EstablishTdx<std::pair<Lhs, Rhs>>::make() {
/* need to ensure Lhs, Rhs are properly reflected */
Reflect::require<Lhs>();
Reflect::require<Rhs>();
return StructTdx::pair<Lhs, Rhs>();
} /*make*/
} /*namespace reflect*/
} /*namespace xo*/
/* end Reflect.hpp */

View file

@ -0,0 +1,31 @@
/* file SelfTagging.hpp
*
* author: Roland Conybeare, Aug 2022
*/
#pragma once
#include "Refcounted.hpp"
#include "TypeDescr.hpp"
#include "TaggedRcptr.hpp"
namespace xo {
namespace reflect {
/* a self-tagging object uses reflection to preserve type information
* until runtime. Can use the reflected information to traverse
* object representation (e.g. for printing / serialization)
* without repetitive/bulky boilerplate.
*
* For pybind11 need to have concrete (non-template) apis,
* helpful to have various classes inherit SelfTagging
*
* For example see [printjson/PrintJson.hpp]
*/
class SelfTagging : public ref::Refcount {
public:
virtual TaggedRcptr self_tp() = 0;
}; /*SelfTagging*/
} /*namespace reflect*/
} /*namespace xo*/
/* end SelfTagging.hpp */

View file

@ -0,0 +1,161 @@
/* @file StructReflector.hpp */
#pragma once
#include "Reflect.hpp"
#include "TypeDescr.hpp"
#include "struct/StructMember.hpp"
#include "struct/StructTdx.hpp"
#include <vector>
namespace xo {
namespace reflect {
template<typename StructT, bool IsSelfTaggingDescendant>
class SelfTagger {};
template<typename StructT>
struct SelfTagger<StructT, true> {
static TaggedPtr self_tp(void * object) {
return (reinterpret_cast<StructT *>(object))->self_tp();
}
};
template<typename StructT>
struct SelfTagger<StructT, false> {
static TaggedPtr self_tp(void * /*object*/) { assert(false); return TaggedPtr::universal_null(); }
};
/* RAII pattern for reflecting a struct.
*
* Use:
* struct Foo { int x_; double y_; };
*
* StructReflector<Foo> sr;
* REFLECT_LITERAL_MEMBER(sr, x_);
* REFLECT_LITERAL_MEMBER(sr, y_);
*
* // optional: regardless, reflection will be completed when sr goes out of scope
* sr.require_complete();
*/
template<typename StructT>
class StructReflector {
public:
using struct_t = StructT;
public:
StructReflector() : td_{EstablishTypeDescr::establish<StructT>()} {}
~StructReflector() {
this->require_complete();
}
bool is_complete() const { return s_reflected_flag; }
bool is_incomplete() const { return !s_reflected_flag; }
template<typename OwnerT, typename MemberT>
void reflect_member(std::string const & member_name,
MemberT OwnerT::* member_addr) {
auto accessor
(GeneralStructMemberAccessor<StructT, OwnerT, MemberT>::make(member_addr));
/* used to do this in GeneralStructMemberAccessor<> ctor,
* but that introduces #include cycle
*/
Reflect::require<MemberT>();
this->member_v_.emplace_back(member_name, std::move(accessor));
} /*reflect_member*/
void require_complete() {
if(!s_reflected_flag) {
s_reflected_flag = true;
constexpr bool have_to_self_tp = std::is_base_of_v<SelfTagging, StructT>;
/* if self-tagging, can use .self_tp() to get most-derived tagged pointer */
auto to_self_tp_fn
= ([](void * object)
{
return SelfTagger<StructT, have_to_self_tp>::self_tp(object);
});
auto tdx = StructTdx::make(std::move(this->member_v_),
have_to_self_tp,
to_self_tp_fn);
this->td_->assign_tdextra(std::move(tdx));
}
} /*complete*/
template<typename AncestorT>
void adopt_ancestors() {
assert(Reflect::is_reflected<AncestorT>());
TypeDescr ancestor_td = Reflect::require<AncestorT>();
/* requires that reflection of AncestorT has completed */
{
assert(ancestor_td->is_struct());
assert(ancestor_td->complete_flag());
}
/* for structs,
* we know that object argument to TypeDescr::n_child() is unused
*/
for (uint32_t i = 0, n = ancestor_td->n_child(nullptr); i < n; ++i) {
StructMember const & member = ancestor_td->struct_member(i);
this->member_v_.push_back(member.for_descendant<StructT, AncestorT>());
}
} /*adopt_ancestors*/
private:
/* set irrevocably to true when .complete() runs.
*
* want to reflect a particular type once;
* short-circuit 2nd or later attempts on the same type
*/
static bool s_reflected_flag;
/* type description object for StructT */
TypeDescrW td_;
/* members of StructT (at least those we're choosing to reflect) */
std::vector<StructMember> member_v_;
}; /*StructReflector*/
template<typename StructT>
bool StructReflector<StructT>::s_reflected_flag = false;
} /*namespace reflect*/
/* e.g.
* struct Foo { int bar_; };
* struct Bar : public Foo { .. };
*
* StructReflector<Bar> sr;
* REFLECT_EXPLICIT_MEMBER(sr, "bar", &Foo::bar_);
*/
#define REFLECT_EXPLICIT_MEMBER(sr, member_name, member) sr.reflect_member(member_name, member)
/* e.g.
* struct Foo { int bar_; };
*
* StructReflector<Foo> sr;
* REFLECT_LITERAL_MEMBER(sr, bar_);
*
* then REFLECT_LITERAL_MEMBER() expands to something like:
* sr.reflect_member("bar_", &StructReflector<Foo>::struct_t::bar_)
*/
#define REFLECT_LITERAL_MEMBER(sr, member_name) sr.reflect_member(#member_name, &decltype(sr)::struct_t::member_name)
/* like REFLECT_LITERAL_MEMBER(), but append trailing underscore
*
* minor convenience, so we can write
* struct Foo { int bar_; };
*
* StructReflector<Foo> sr;
* REFLECT_MEMBER(sr, bar); // reflects Foo::bar_ as "bar"
*/
#define REFLECT_MEMBER(sr, member_name) sr.reflect_member(#member_name, &decltype(sr)::struct_t::member_name##_)
} /*namespace xo*/

View file

@ -0,0 +1,122 @@
/* @file TaggedPtr.hpp */
#pragma once
#include "TypeDescr.hpp"
#include <unordered_set>
namespace xo {
namespace reflect {
class TaggedRcptr; /* see [reflect/TaggedRcptr.hpp] */
class TaggedPtr {
public:
TaggedPtr(TypeDescr td, void * x) : td_{td}, address_{x} {}
static TaggedPtr universal_null() { return TaggedPtr(nullptr, nullptr); }
/* would be clean to put make() here;
* however it leads to cyclic #include paths,
* so put it elsewhere
*/
#ifdef NOT_USING
template<typename T>
static TaggedPtr make(T * x) { return TaggedPtr(Reflect::require<T>(), x); }
#endif
/* visit an object tree. calls preorder_visit_fn() on tp,
* and all objects reachable directly-or-indirectly from tp.
* will call preorder_visit_fn() multiple times if there are multiple paths
* to a node.
*
* require: no cycles in object graph -- undefined behavior if a cycle is present
*/
template<typename Fn>
static void visit_tree_preorder(TaggedPtr tp, Fn && preorder_visit_fn) {
using std::uint32_t;
preorder_visit_fn(tp);
for(uint32_t i = 0, n = tp.n_child(); i < n; ++i) {
visit_tree_preorder(tp.get_child(i), preorder_visit_fn);
}
} /*visit_tree_preorder*/
/* visit object graph. calls preorder_visit_fn() on tp in depth-first
* order. detects and silently prunes duplicate/cyclic references.
*/
template<typename Fn>
static void visit_graph(TaggedPtr tp, Fn && visit_fn) {
std::unordered_set<void *> visited_set;
visit_graph_aux(tp, visit_fn, &visited_set);
} /*visit_graph*/
TypeDescr td() const { return td_; }
void * address() const { return address_; }
void assign_td(TypeDescr x) { td_ = x; }
void assign_address(void * x) { address_ = x; }
bool is_universal_null() const { return (td_ == nullptr) && (address_ == nullptr); }
bool is_vector() const { return td_ && td_->is_vector(); }
bool is_struct() const { return td_ && td_->is_struct(); }
/* returns pointer-to-T, if in fact this tagged pointer is understood
* to refer to a T-instance; otherwise nullptr
*/
template<typename T>
T * recover_native() const { return this->td_->recover_native<T>(this->address_); }
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*/
/* require:
* - .is_struct() is true
*/
std::string const & struct_member_name(uint32_t i) const {
return this->td_->struct_member_name(i);
}
private:
template<typename Fn>
static void visit_graph_aux(TaggedPtr tp,
Fn && visit_fn,
std::unordered_set<void *> * p_visited_set)
{
if (tp.address() == nullptr)
return;
if (p_visited_set->find(tp.address()) == p_visited_set->end()) {
p_visited_set->insert(tp.address());
visit_fn(tp);
for (uint32_t i = 0, n = tp.n_child(); i < n; ++i) {
visit_graph_aux(tp.get_child(i), visit_fn, p_visited_set);
}
}
} /*visit_graph_aux*/
private:
friend class TaggedRcptr;
private:
/* describes the actual type stored at *address.
* can be null if .address is null
*/
TypeDescr td_;
/* address with type information preserved at runtime */
void * address_;
}; /*TaggedPtr*/
} /*namespace reflect*/
} /*namespace xo*/
/* end TaggedPtr.hpp */

View file

@ -0,0 +1,88 @@
/* file TaggedRcptr.hpp
*
* author: Roland Conybeare, Aug 2022
*/
#pragma once
#include "TaggedPtr.hpp"
// causes #include cycle, reflect/Reflect.hpp includes this header
//#include "reflect/Reflect.hpp"
#include "refcnt/Refcounted.hpp"
namespace xo {
namespace reflect {
/* Tagged reference-counted pointer.
* Like TaggedPtr, but also maintains reference count.
*
* note that refcounting behavior is lost if assigned to a TaggedPtr variable!
*/
class TaggedRcptr : public TaggedPtr {
public:
using Refcount = ref::Refcount;
public:
TaggedRcptr(TypeDescr td, Refcount * x) : TaggedPtr(td, x) {
ref::intrusive_ptr_add_ref(x);
}
TaggedRcptr(TaggedRcptr const & x) : TaggedPtr(x) {
ref::intrusive_ptr_add_ref(x.rc_address());
}
TaggedRcptr(TaggedRcptr && x) : TaggedPtr(std::move(x)) {
/* since we're moving from x, need to make sure x.dtor
* doesn't decrement refcount
*/
x.assign_address(nullptr);
}
~TaggedRcptr() {
ref::intrusive_ptr_release(this->rc_address());
}
/* causes #include cycle, see [reflect/Reflect.hpp] */
#ifdef NOT_IN_USE
/* require: T --isa--> ref::Refcount */
template<typename T>
static TaggedRcptr make(T * x) { return TaggedRcptr(Reflect::require<T>(), x); }
#endif
Refcount * rc_address() const {
return reinterpret_cast<Refcount *>(this->address());
} /*rc_address*/
TaggedRcptr & operator=(TaggedRcptr const & rhs) {
Refcount * x = rhs.rc_address();
Refcount * old = this->rc_address();
TaggedPtr::operator=(rhs);
if (x != old) {
intrusive_ptr_release(old);
intrusive_ptr_add_ref(x);
}
return *this;
} /*operator=*/
TaggedRcptr & operator=(TaggedRcptr && rhs) {
/* swap pointers + type descriptions;
* then don't need to touch refcounts
*/
std::swap(this->td_, rhs.td_);
std::swap(this->address_, rhs.address_);
return *this;
} /*operator=*/
void display(std::ostream & os) const;
std::string display_string() const;
}; /*TaggedRcptr*/
inline std::ostream & operator<<(std::ostream & os, TaggedRcptr const & x) {
x.display(os);
return os;
} /*operator<<*/
} /*namespace reflect*/
} /*namespace xo*/
/* end TaggedRcptr.hpp */

View file

@ -0,0 +1,302 @@
/* @file TypeDescr.hpp */
#pragma once
//#include "reflect/atomic/AtomicTdx.hpp"
#include "TypeDescrExtra.hpp"
#include "cxxutil/demangle.hpp"
#include <iostream>
#include <typeinfo>
#include <unordered_map>
#include <vector>
#include <string_view>
#include <memory>
#include <cstring>
#include <cstdint>
#include <cassert>
namespace xo {
namespace reflect {
class TaggedPtr; /* see [reflect/TaggedPtr.hpp] */
/* A reflected type is a type for which we keep information around at runtime
* Assign reflected types unique (within an executable) ids,
* allocating consecutively, starting from 1.
* Reserve 0 as a sentinel
*/
class TypeId {
public:
/* allocate a new TypeId value.
* promise:
* - retval.id() > 0
*/
static TypeId allocate() { return TypeId(s_next_id++); }
std::uint32_t id() const { return id_; }
private:
explicit TypeId(std::uint32_t id) : id_{id} {}
private:
static std::uint32_t s_next_id;
/* unique index# for this type.
* 0 reserved for sentinel
*/
std::uint32_t id_ = 0;
}; /*TypeId*/
inline std::ostream &
operator<<(std::ostream & os, TypeId x) {
os << x.id();
return os;
} /*operator<<*/
/* runtime description of a struct/class instance variable */
class StructMember;
class TypeDescrBase;
using TypeDescr = TypeDescrBase const *;
using TypeDescrW = TypeDescrBase *;
/* convenience wrapper for a std::type_info pointer.
* works properly with pybind11, since python doens't encounter
* native type_info pointer, it won't try to delete it.
*/
class TypeInfoRef {
public:
explicit TypeInfoRef(std::type_info const * tinfo) : tinfo_{tinfo} {}
TypeInfoRef(TypeInfoRef const & x) = default;
/* use:
* TypeInfoRef tinfo = TypeInfoRef::make<T>();
*/
template<typename T>
TypeInfoRef make() { return TypeInfoRef(&typeid(T)); }
std::size_t hash_code() const { return this->tinfo_->hash_code(); }
char const * impl_name() const { return this->tinfo_->name(); }
static bool is_equal(TypeInfoRef x, TypeInfoRef y) noexcept {
if (x.hash_code() != y.hash_code())
return false;
return ::strcmp(x.impl_name(), y.impl_name()) == 0;
} /*is_equal*/
private:
/* native type_info object for encapsulated type */
std::type_info const * tinfo_ = nullptr;
}; /*TypeInfoRef*/
} /*namespace reflect*/
} /*namespace xo*/
namespace std {
template <> struct hash<xo::reflect::TypeInfoRef> {
std::size_t operator()(xo::reflect::TypeInfoRef x) const noexcept { return x.hash_code(); }
};
} /*namespace std*/
namespace xo {
namespace reflect {
inline bool operator==(TypeInfoRef x, TypeInfoRef y) { return TypeInfoRef::is_equal(x, y); }
inline bool operator!=(TypeInfoRef x, TypeInfoRef y) { return !TypeInfoRef::is_equal(x, y); }
#ifdef NOT_IN_USE
namespace detail {
class HashTypeInfoRef {
public:
std::size_t operator()(TypeInfoRef x) const noexcept { return x.hash_code(); }
}; /*HashTypeInfoRef*/
class EqualTypeInfoRef {
public:
bool operator()(TypeInfoRef x, TypeInfoRef y) const noexcept { return TypeInfoRef::is_equal(x, y); }
}; /*EqualTypeInfoRef*/
} /*namespace detail*/
#endif
class TypeDescrExtra;
/* run-time description for a native c++ type */
class TypeDescrBase {
public:
/* type-description objects for a type T is unique,
* --> can always use its address
*/
TypeDescrBase(TypeDescrBase const & x) = delete;
/* test whether a type has been reflected.
* introducing this for unit testing
*/
static bool is_reflected(std::type_info const * tinfo) {
return (s_type_table_map.find(TypeInfoRef(tinfo))
!= s_type_table_map.end());
} /*is_reflected*/
/* NOTE:
* implementation here will be defeated if std::type_info
* objects violate ODR. This occurs with clang + 2-level namespaces,
* so important to linke with --flat_namespace defined.
* See FAQ
* [Build Issues|Q2 - dynamic_cast<Foo<*>> fails]
*/
static TypeDescrW require(std::type_info const * tinfo,
std::string_view canonical_name,
std::unique_ptr<TypeDescrExtra> tdextra);
/* print table of reflected types to os */
static void print_reflected_types(std::ostream & os);
TypeId id() const { return id_; }
std::type_info const * typeinfo() const { return 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.
*
* Warning: comparing typeinfo address can give false negatives.
* suspect this is caused by problems coalescing linker symbols
* in the clang toolchain.
*/
template<typename T>
bool is_native() const {
return ((this->typeinfo() == &typeid(T))
|| (this->typeinfo()->hash_code() == typeid(T).hash_code())
|| (this->typeinfo()->name() == typeid(T).name()));
} /*is_native*/
/* safe downcast -- like dynamic_cast<>, but does not require a source type */
template<typename T>
T * recover_native(void * address) const {
if (this->is_native<T>()) {
return reinterpret_cast<T *>(address);
} else {
return nullptr;
}
} /*recover_native*/
bool is_vector() const { return this->tdextra_->is_vector(); }
bool is_struct() const { return this->tdextra_->is_struct(); }
/* given a T-instance object, return tagged pointer with T replaced
* by the most-derived-subtype of T to which *object belongs.
* This works only for descendants of reflect::SelfTagging
*/
TaggedPtr most_derived_self_tp(void * object) const;
/* if generalized vector (std::vector<T>, std::array<T,N>, ..):
* .n_child() reports #of elements
* if struct/class:
* .n_child() reports #of instance variables (that have been reflected)
*/
uint32_t n_child(void * object) const { return this->tdextra_->n_child(object); }
TaggedPtr child_tp(uint32_t i, void * object) const;
/* require:
* - .is_struct() = true
* - i in [0 .. .n_child() - 1]
*/
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:
* - .is_struct() = true
* - i in [0 .. .n_child() - 1]
*/
StructMember const & struct_member(uint32_t i) const {
StructMember const * sm = this->tdextra_->struct_member(i);
assert(sm);
return *sm;
} /*struct_member*/
void display(std::ostream & os) const;
std::string display_string() const;
/* mark this TypeDescr complete;
* returns the value of .complete_flag from _before_
* this call
*/
bool mark_complete();
/* call this once to attach extended type information to a type-description
* (e.g. description of struct members for a record type)
*/
void assign_tdextra(std::unique_ptr<TypeDescrExtra> tdx);
private:
TypeDescrBase(TypeId id,
std::type_info const * tinfo,
std::string_view canonical_name,
std::unique_ptr<TypeDescrExtra> tdextra);
private:
/* invariant:
* - for all TypeDescrImpl instances x:
* - s_type_table_v[x->id()] = x
* - s_type_table_map[TypeInfoRef(x->typeinfo())] = x
*/
/* hashmap of all TypeDescr instances, indexed by . singleton */
static std::unordered_map<TypeInfoRef, std::unique_ptr<TypeDescrBase>> s_type_table_map;
/* hashmap of (presumed) duplicate TypeInfoRef values.
* This happens with clang sometimes when the same type is referenced
* from multiple modules (i.e. shared libs).
*/
static std::unordered_map<TypeInfoRef, TypeDescrBase *> s_coalesced_type_table_map;
/* vector of all TypeDescr instances. singleton. */
static std::vector<TypeDescrBase *> s_type_table_v;
private:
/* unique id# for this type */
TypeId id_;
/* typeinfo for type T */
std::type_info const * typeinfo_ = nullptr;
/* canonical name for this type (see demangle.hpp for type_name<T>())
* e.g.
* xo::option::Px2
*/
std::string_view canonical_name_;
/* suffix of .canonical_name, just after last ':'
* e.g.
* Px2
*/
std::string_view short_name_;
/* set to true once final value for .tdextra is established
* intially all TypeDescr objects will use AtomicTdx for .tdextra
* Reflect::require() upgrades .tdextra for particular types.
* When that procedure makes a decision for a type T,
* .complete_flag will be set to true for the corresponding TypeDescrBase instance
*/
bool complete_flag_ = false;
/* additional type information that either:
* (a) isn't universal across all types,
* e.g. dereferencing instance of a pointer type
* (b) can't be captured with template-fu,
* e.g. struct member names
*
* generally .tdextra will be populated some time after TypeDescrBase's ctor exits.
* This is necessary because of (b) above, also because of possibility of recursive
* types.
*/
std::unique_ptr<TypeDescrExtra> tdextra_;
}; /*TypeDescrBase*/
inline std::ostream &
operator<<(std::ostream & os, TypeDescrBase const & x) {
x.display(os);
return os;
} /*operator<<*/
} /*namespace reflect*/
} /*namespace xo*/
/* end TypeDescr.hpp */

View file

@ -0,0 +1,65 @@
/* @file TypeDescrExtra.hpp */
#pragma once
#include "Metatype.hpp"
#include <string>
/* note: this file #include'd into TypeDescr.hpp */
#include <cstdint>
namespace xo {
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;
public:
virtual ~TypeDescrExtra() = default;
bool is_vector() const { return this->metatype() == Metatype::mt_vector; }
bool is_struct() const { return this->metatype() == Metatype::mt_struct; }
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*/
} /*namespace xo*/
/* end TypeDescrExtra.hpp */

View file

@ -0,0 +1,47 @@
/* @file TypeDrivenMap.hpp
*
* author: Roland Conybeare, Aug 2022
*/
#pragma once
#include "reflect/TypeDescr.hpp"
#include <vector>
namespace xo {
namespace reflect {
/* represents a map :: TypeId -> Value */
template<typename Value>
class TypeDrivenMap {
public:
Value const * lookup(TypeId id) const { return this->lookup_slot(id); }
Value * require(TypeId id) { return this->require_slot(id); }
Value * require(TypeDescr td) { return this->require_slot(td->id()); }
private:
Value const * lookup_slot(TypeId id) const {
if (this->contents_v_.size() <= id.id())
return nullptr;
return &(this->contents_v_[id.id()]);
} /*lookup_slot*/
Value * require_slot(TypeId id) {
if (this->contents_v_.size() <= id.id())
this->contents_v_.resize(id.id() + 1);
return &(this->contents_v_[id.id()]);
} /*require_slot*/
private:
/* since TypeId/s are unique, compact sequence numbers,
* can efficiently store mapping to Values using a vector indexed by TypeId
*/
std::vector<Value> contents_v_;
}; /*TypeDrivenMap*/
} /*namespace reflect*/
} /*namespace xo*/
/* end TypeDrivenMap.hpp */

View file

@ -0,0 +1,37 @@
/* @file AtomicTdx.hpp */
#pragma once
#include "xo/reflect/TypeDescrExtra.hpp"
//#include "reflect/TaggedPtr.hpp"
#include <memory>
namespace xo {
namespace reflect {
class TaggedPtr;
/* Extra type-associated information for an atomic type.
* We use this as degenerate catch-all case for types that aren't known
* to have additional structure (std::vector, std::map, int*, etc.)
*/
class AtomicTdx : public TypeDescrExtra {
public:
virtual ~AtomicTdx() = default;
static std::unique_ptr<AtomicTdx> make();
// ----- Inherited from TypeDescrExtra -----
virtual Metatype metatype() const override { return Metatype::mt_atomic; }
virtual uint32_t n_child(void * /*object*/) const override { return 0; }
virtual TaggedPtr child_tp(uint32_t /*i*/, void * /*object*/) const override;
virtual std::string const & struct_member_name(uint32_t i) const override;
//virtual StructMember const * struct_member(uint32_t /*i*/) const override { return nullptr; }
private:
AtomicTdx() = default;
}; /*TypeDescrExtra*/
} /*namespace reflect*/
} /*namespace xo*/
/* end AtomicTdx.hpp */

View file

@ -0,0 +1,22 @@
/* file init_reflect.hpp
*
* author: Roland Conybeare, Sep 2022
*/
#pragma once
#include "subsys/Subsystem.hpp"
namespace xo {
/* tag to represent the reflect/ subsystem within ordered initialization */
enum S_reflect_tag {};
template<>
struct InitSubsys<S_reflect_tag> {
static void init();
static InitEvidence require();
};
} /*namespace xo*/
/* end init_reflect.hpp */

View file

@ -0,0 +1,76 @@
/* file PointerTdx.hpp
*
* author: Roland Conybeare, Sep 2022
*/
#pragma once
#include "xo/reflect/TypeDescrExtra.hpp"
#include "xo/reflect/EstablishTypeDescr.hpp"
#include "indentlog/scope.hpp"
namespace xo {
namespace reflect {
/* Extra type-associated information for a pointer-like type
*
* Treat a pointer as a container that has 0 or 1 children;
* - 0 children if null
* - 1 child otherwise
*/
class PointerTdx : public TypeDescrExtra {
public:
// ----- Inherited from TypeDescrExtra -----
virtual Metatype metatype() const override { return Metatype::mt_pointer; }
virtual uint32_t n_child(void * object) const override = 0;
virtual TaggedPtr child_tp(uint32_t i, void * object) const override = 0;
/* (forbidden) */
virtual std::string const & struct_member_name(uint32_t i) const override;
}; /*PointerTdx*/
// ----- RefPointerTdx -----
/* xo::ref::intrusive_ptr<T> for some T */
template<typename Pointer>
class RefPointerTdx : public PointerTdx {
public:
using target_t = Pointer;
static std::unique_ptr<RefPointerTdx> make() {
return std::unique_ptr<RefPointerTdx>(new RefPointerTdx());
} /*make*/
virtual uint32_t n_child(void * object) const override {
/* e.g:
* target_t = ref::rp<filter::KalmanFilterState>
*/
target_t * ptr = reinterpret_cast<target_t *>(object);
if (*ptr)
return 1;
else
return 0;
} /*n_child*/
virtual TaggedPtr child_tp(uint32_t i, void * object) const override {
using xo::tostr;
using xo::xtag;
target_t * ptr = reinterpret_cast<target_t *>(object);
if (i > 0) {
throw std::runtime_error(tostr("RefPointerTdx<T>::child_tp"
": attempt to fetch child #i from a ref::rp<T>",
xtag("T", type_name<target_t>()),
xtag("i", i),
xtag("n", this->n_child(object))));
}
return establish_most_derived_tp(ptr->get());
} /*child_tp*/
}; /*RefPointerTdx*/
} /*namespace reflect*/
} /*namespace xo*/
/* end PointerTdx.hpp */

View file

@ -0,0 +1,235 @@
/* @file StructMember.hpp */
#pragma once
#include "xo/reflect/TypeDescr.hpp"
#include "xo/reflect/EstablishTypeDescr.hpp"
#include "xo/reflect/TaggedPtr.hpp"
#include <string>
#include <memory>
namespace xo {
namespace reflect {
class AbstractStructMemberAccessor {
public:
virtual ~AbstractStructMemberAccessor() = default;
/* get tagged pointer referring to this member of the object at *struct_addr */
TaggedPtr member_tp(void * struct_addr) const;
/* get type-description object for struct
* containing this member. useful for consistency checking.
*/
virtual TypeDescr struct_td() const = 0;
/* get type-description object for this member
* e.g. if this member represents Foo::bar_ in
* struct Foo { int bar_; };
* then
* .member_td() => Reflect::require<int>();
*/
virtual TypeDescr member_td() const = 0;
/* get address of a particular member, given parent address */
virtual void * address(void * struct_addr) const = 0;
virtual std::unique_ptr<AbstractStructMemberAccessor> clone() const = 0;
}; /*AbstractStructMemberAccessor*/
/* GeneralStructMemberAccessor
*
* Use this to handle access to possibly-inherited struct members:
*
* struct Foo { int x_; }
* struct Bar { char * y_; }
* struct Quux : public Foo, public Bar { bool z_; }
*
* want to be able to access Bar::y from a Quux instance.
* in example, would use GenericStructMemberAccessor<>
* with:
* StructT = Quux,
* OwnerT = Bar,
* MemberT = char*
*
* Require:
* StructT* is assignable to OwnerT* (because StructT --isa--> OwnerT)
*/
template <typename StructT, typename OwnerT, typename MemberT>
class GeneralStructMemberAccessor : public AbstractStructMemberAccessor {
public:
/* pointer to a OwnerT member of type MemberT */
using Memptr = MemberT OwnerT::*;
public:
GeneralStructMemberAccessor(Memptr memptr) : member_td_{EstablishTypeDescr::establish<MemberT>()},
memptr_{memptr} {}
GeneralStructMemberAccessor(GeneralStructMemberAccessor const & x) = default;
virtual ~GeneralStructMemberAccessor() = default;
static std::unique_ptr<GeneralStructMemberAccessor> make(Memptr memptr) {
return std::unique_ptr<GeneralStructMemberAccessor>(new GeneralStructMemberAccessor(memptr)); }
/* get member address given address of parent struct
* (i.e. from Struct*, not from OwnerT*)
*/
MemberT * address_impl(StructT * self_addr) const {
OwnerT * owner_addr = self_addr;
return &(owner_addr->*memptr_);
} /*address_impl*/
// ----- Inherited from AbstractStructMemberAccessor -----
#ifdef OBSOLETE
virtual TaggedPtr member_tp(void * struct_addr) const override {
/* FIXME: this reports declared type of member, instead of
* (possibly narrower) actual type of member
*/
return this->member_td_->most_derived_self_tp(this->address(struct_addr));
//return TaggedPtr(this->member_td_, this->address(struct_addr));
} /*member_tp*/
#endif
virtual TypeDescr struct_td() const override { return EstablishTypeDescr::establish<StructT>(); }
virtual TypeDescr member_td() const override { return this->member_td_; }
virtual void * address(void * struct_addr) const override {
return this->address_impl(reinterpret_cast<StructT *>(struct_addr));
} /*address*/
virtual std::unique_ptr<AbstractStructMemberAccessor> clone() const override {
return std::unique_ptr<AbstractStructMemberAccessor>
(new GeneralStructMemberAccessor(*this));
} /*clone*/
private:
/* type description for MemberT; .memptr is pointer-to-member-of-OwnerT,
* where that member has type MemberT
*/
TypeDescr member_td_ = nullptr;
/* pointer to member of OwnerT */
Memptr memptr_ = nullptr;
}; /*GeneralStructMemberAccessor*/
/* struct-member accessor via delegation,
* to accessor of a parent (or some other ancestor) class.
*
* struct Foo { int x_; }
* struct Bar { char * y_; }
*
* auto bar_x_access = GeneralStructMemberAccessor<Bar, Foo, int>::make(&Foo::x_);
*
* or equivalently:
* auto foo_x_access = GeneralStructMemberAccessor<Foo, Foo, int>::make(&Foo::x_);
* auto bar_x_access = AncestorStructMemberAccessor<Bar, Foo>::adopt(foo_x_access);
*
* can use the 2nd form to adopt accessors from an already-reflected ancestor class
*
* Require:
* - StructT -isa-> AncestorT
*/
template <typename StructT, typename AncestorT>
class AncestorStructMemberAccessor : public AbstractStructMemberAccessor {
public:
AncestorStructMemberAccessor(std::unique_ptr<AbstractStructMemberAccessor> ancestor_accessor)
: ancestor_accessor_{std::move(ancestor_accessor)} {}
AncestorStructMemberAccessor(AncestorStructMemberAccessor const & x) = default;
virtual ~AncestorStructMemberAccessor() = default;
static std::unique_ptr<AncestorStructMemberAccessor>
adopt(std::unique_ptr<AbstractStructMemberAccessor> ancestor_accessor) {
return std::unique_ptr<AncestorStructMemberAccessor>
(new AncestorStructMemberAccessor(std::move(ancestor_accessor)));
} /*adopt*/
void * address_impl(StructT * self_addr) const {
/* to use access-via-ancestor, need to convert to ancestor pointer */
AncestorT * ancestor_addr = self_addr;
return this->ancestor_accessor_->address(ancestor_addr);
} /*address_impl*/
// ----- inherited from AbstractStructMemberAccessor -----
#ifdef OBSOLETE
virtual TaggedPtr member_tp(void * struct_addr) const override {
AncestorT * ancestor_addr = reinterpret_cast<StructT *>(struct_addr);
return this->ancestor_accessor_->member_tp(ancestor_addr);
} /*member_tp*/
#endif
virtual TypeDescr struct_td() const override { return EstablishTypeDescr::establish<StructT>(); }
virtual TypeDescr member_td() const override { return this->ancestor_accessor_->member_td(); }
virtual void * address(void * struct_addr) const override {
return this->address_impl(reinterpret_cast<StructT *>(struct_addr));
}
virtual std::unique_ptr<AbstractStructMemberAccessor> clone() const override {
return std::unique_ptr<AbstractStructMemberAccessor>
(new AncestorStructMemberAccessor(std::move(this->ancestor_accessor_->clone())));
} /*clone*/
private:
/* .ancestor_accessor fetches some particular member of AncestorT */
std::unique_ptr<AbstractStructMemberAccessor> ancestor_accessor_;
}; /*AncestorStructMemberAccessor*/
/* describes a member of a struct/class
* see [reflect/StructReflector.hpp]
*/
class StructMember {
public:
StructMember() = default;
StructMember(std::string const & name,
std::unique_ptr<AbstractStructMemberAccessor> accessor)
: member_name_{name}, accessor_{std::move(accessor)} {}
StructMember(StructMember && x)
: member_name_{std::move(x.member_name_)},
accessor_{std::move(x.accessor_)} {}
static StructMember null();
std::string const & member_name() const { return member_name_; }
TaggedPtr get_member_tp(void * struct_addr) const { return this->accessor_->member_tp(struct_addr); }
TypeDescr get_struct_td() const { return this->accessor_->struct_td(); }
TypeDescr get_member_td() const { return this->accessor_->member_td(); }
//void * get_member_addr(void * struct_addr) const { return this->accessor_->address(struct_addr); }
/* make copy that accesses this member, but starting
* from pointer to some derived class DescendantT,
* instead of from container type StructT known to (but not exposed by) *this
*/
template <typename DescendantT, typename StructT>
StructMember for_descendant() const {
assert(EstablishTypeDescr::establish<StructT>() == this->get_struct_td());
return StructMember(this->member_name(),
std::move(AncestorStructMemberAccessor<DescendantT, StructT>::adopt
(std::move(this->accessor_->clone()))));
} /*for_descendant*/
StructMember & operator=(StructMember && x) {
member_name_ = std::move(x.member_name_);
accessor_ = std::move(x.accessor_);
return *this;
}
private:
/* member name, e.g. foo if
* struct StructT { MemberT foo; }
*/
std::string member_name_;
/* T recd;
* this->accessor_->address_impl(&recd) ==> &(recd.member)
*/
std::unique_ptr<AbstractStructMemberAccessor> accessor_;
}; /*StructMember*/
} /*namespace reflect*/
} /*namespace xo*/
/* end StructMember.hpp */

View file

@ -0,0 +1,95 @@
/* @file StructTdx.hpp */
#pragma once
#include "xo/reflect/TypeDescrExtra.hpp"
#include "xo/reflect/TaggedPtr.hpp"
#include "StructMember.hpp"
//#include "xo/reflect/struct/StructMember.hpp"
#include <vector>
#include <functional>
#include <memory>
namespace xo {
namespace reflect {
/* Extra type-associated information for a struct/class.
* We use this to preserve information about memory layout
* at runtime
*/
class StructTdx : public TypeDescrExtra {
public:
/* named ctor idiom. create new instance for struct with given member list
*
* to_self_tp. use this function to support .most_derived_self_tp()
*/
static std::unique_ptr<StructTdx> make(std::vector<StructMember> member_v,
bool have_to_self_tp,
std::function<TaggedPtr (void *)> to_self_tp);
/* specialization for std::pair<Lhs, Rhs>
* coordinates with [reflect/Reflect.hpp]
*/
template<typename Lhs, typename Rhs>
static std::unique_ptr<StructTdx> pair() {
using struct_t = std::pair<Lhs, Rhs>;
std::vector<StructMember> mv;
{
auto lhs_access
(GeneralStructMemberAccessor<struct_t, struct_t, Lhs>::make
(&struct_t::first));
mv.push_back(StructMember("first", std::move(lhs_access)));
}
{
auto rhs_access
(GeneralStructMemberAccessor<struct_t, struct_t, Rhs>::make
(&struct_t::second));
mv.push_back(StructMember("second", std::move(rhs_access)));
}
std::function<TaggedPtr (void *)> null_to_self_tp;
return make(std::move(mv),
false /*!have_to_self_tp*/,
null_to_self_tp);
} /*pair*/
// ----- Inherited from TypeDescrExtra -----
virtual Metatype metatype() const override { return Metatype::mt_struct; }
virtual TaggedPtr most_derived_self_tp(TypeDescrBase const * object_td,
void * object) const override {
if (this->have_to_self_tp_) {
return this->to_self_tp_(object);
} else {
return TypeDescrExtra::most_derived_self_tp(object_td, object);
}
}
virtual uint32_t n_child(void * /*object*/) const override { return this->member_v_.size(); }
virtual TaggedPtr child_tp(uint32_t i, void * object) const override;
virtual std::string const & struct_member_name(uint32_t i) const override;
virtual StructMember const * struct_member(uint32_t i) const override;
private:
StructTdx(std::vector<StructMember> member_v,
bool have_to_self_tp,
std::function<TaggedPtr (void*)> to_self_tp)
: member_v_{std::move(member_v)},
have_to_self_tp_{have_to_self_tp},
to_self_tp_{std::move(to_self_tp)} {}
private:
/* per-instance-variable reflection details */
std::vector<StructMember> member_v_;
/* true if .to_self_tp() is defined */
bool have_to_self_tp_ = false;
/* get TaggedPtr for most-derived subtype of supplied T-instance */
std::function<TaggedPtr (void *)> to_self_tp_;
}; /*StructTdx*/
} /*namespace reflect*/
} /*namespace xo*/
/* end StructTdx.hpp */

View file

@ -0,0 +1,96 @@
/* file VectorTdx.hpp
*
* author: Roland Conybeare, Aug 2022
*/
#pragma once
#include "xo/reflect/TypeDescrExtra.hpp"
#include "xo/reflect/EstablishTypeDescr.hpp"
namespace xo {
namespace reflect {
/* Extra type-associated information for a vector/array.
*/
class VectorTdx : public TypeDescrExtra {
public:
/* named ctor idiom. create new instance for a vector type */
//static std::unique_ptr<VectorTdx> make();
// ----- Inherited from TypeDescrExtra -----
virtual Metatype metatype() const override { return Metatype::mt_vector; }
virtual uint32_t n_child(void * object) const override = 0;
virtual TaggedPtr child_tp(uint32_t i, void * object) const override = 0;
/* (forbidden) */
virtual std::string const & struct_member_name(uint32_t i) const override;
}; /*VectorTdx*/
// ----- StlVectorTdx -----
/* require:
* - VectorT.size()
* - VectorT[int] :: lvalue
*/
template<typename VectorT>
class StlVectorTdx : public VectorTdx {
public:
using target_t = VectorT;
static std::unique_ptr<StlVectorTdx> make() {
return std::unique_ptr<StlVectorTdx>(new StlVectorTdx());
} /*make*/
virtual uint32_t n_child(void * object) const override {
target_t * vec = reinterpret_cast<target_t *>(object);
return vec->size();
} /*n_child*/
virtual TaggedPtr child_tp(uint32_t i, void * object) const override {
target_t * vec = reinterpret_cast<target_t *>(object);
return establish_most_derived_tp(&((*vec)[i]));
} /*child_tp*/
}; /*StlVectorTdx*/
// ----- std::array<Element, N> -----
/* coordinates with EstablishTdx<std::array<Element, N>>::make(),
* see [reflect/Reflect.hpp]
*/
template<typename Element, std::size_t N>
using StdArrayTdx = StlVectorTdx<std::array<Element, N>>;
// ----- std::vector<Element> -----
/* coordinates with EstablishTdx<std::vector<Element>>::make()
* see [reflect/Reflect.hpp]
*/
template<typename Element>
class StdVectorTdx : public VectorTdx {
public:
using target_t = std::vector<Element>;
static std::unique_ptr<StdVectorTdx> make() {
return std::unique_ptr<StdVectorTdx>(new StdVectorTdx());
} /*make*/
virtual uint32_t n_child(void * object) const override {
target_t * vec = reinterpret_cast<target_t *>(object);
return vec->size();
} /*n_child*/
virtual TaggedPtr child_tp(uint32_t i, void * object) const override {
target_t * vec = reinterpret_cast<target_t *>(object);
return establish_most_derived_tp(&((*vec)[i]));
}
}; /*StdVectorTdx*/
} /*namespace reflect*/
} /*namespace xo*/
/* end VectorTdx.hpp */