Add 'xo-reflect/' from commit '5dce303fe1'
git-subtree-dir: xo-reflect git-subtree-mainline:8111f3846cgit-subtree-split:5dce303fe1
This commit is contained in:
commit
820f431d66
45 changed files with 3834 additions and 0 deletions
117
xo-reflect/.github/workflows/cmake-single-platform.yml
vendored
Normal file
117
xo-reflect/.github/workflows/cmake-single-platform.yml
vendored
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
name: xo-reflect ubuntu build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||||
|
BUILD_TYPE: Release
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
# The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
|
||||||
|
# You can convert this to a matrix build if you need cross-platform coverage.
|
||||||
|
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: checkout source
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install catch2
|
||||||
|
# install catch2. see [[https://stackoverflow.com/questions/57982945/how-to-apt-get-install-in-a-github-actions-workflow]]
|
||||||
|
run: sudo apt-get install -y catch2
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Clone xo-cmake
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: Rconybea/xo-cmake
|
||||||
|
path: repo/xo-cmake
|
||||||
|
|
||||||
|
- name: Configure xo-cmake
|
||||||
|
run: cmake -B ${{github.workspace}}/build_xo-cmake -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/xo-cmake
|
||||||
|
|
||||||
|
- name: Build xo-cmake (trivial)
|
||||||
|
run: cmake --build ${{github.workspace}}/build_xo-cmake --config ${{env.BUILD_TYPE}}
|
||||||
|
|
||||||
|
- name: Install xo-cmake
|
||||||
|
run: cmake --install ${{github.workspace}}/build_xo-cmake
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Clone indentlog
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: Rconybea/indentlog
|
||||||
|
path: repo/indentlog
|
||||||
|
|
||||||
|
- name: Configure indentlog
|
||||||
|
# configure cmake for indentlog in dedicated build directory.
|
||||||
|
run: cmake -B ${{github.workspace}}/build_indentlog -DCMAKE_MODULE_PATH=${{github.workspace}}/local/share/cmake -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/indentlog
|
||||||
|
|
||||||
|
- name: Build indentlog
|
||||||
|
run: cmake --build ${{github.workspace}}/build_indentlog --config ${{env.BUILD_TYPE}}
|
||||||
|
|
||||||
|
- name: Install indentlog
|
||||||
|
# install into ${{github.workspace}}/local
|
||||||
|
run: cmake --install ${{github.workspace}}/build_indentlog
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Clone subsys
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: Rconybea/subsys
|
||||||
|
path: repo/subsys
|
||||||
|
|
||||||
|
- name: Configure subsys
|
||||||
|
# configure cmake for subsys in dedicated build directory.
|
||||||
|
run: cmake -B ${{github.workspace}}/build_subsys -DCMAKE_MODULE_PATH=${{github.workspace}}/local/share/cmake -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/subsys
|
||||||
|
|
||||||
|
- name: Build subsys
|
||||||
|
run: cmake --build ${{github.workspace}}/build_subsys --config ${{env.BUILD_TYPE}}
|
||||||
|
|
||||||
|
- name: Install subsys
|
||||||
|
# install into ${{github.workspace}}/local
|
||||||
|
run: cmake --install ${{github.workspace}}/build_subsys
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Clone refcnt
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: Rconybea/refcnt
|
||||||
|
path: repo/refcnt
|
||||||
|
|
||||||
|
- name: Configure refcnt
|
||||||
|
# configure cmake for refcnt in dedicated build directory.
|
||||||
|
run: cmake -B ${{github.workspace}}/build_refcnt -DCMAKE_MODULE_PATH=${{github.workspace}}/local/share/cmake -DCMAKE_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/refcnt
|
||||||
|
|
||||||
|
- name: Build refcnt
|
||||||
|
run: cmake --build ${{github.workspace}}/build_refcnt --config ${{env.BUILD_TYPE}}
|
||||||
|
|
||||||
|
- name: Install refcnt
|
||||||
|
# install into ${{github.workspace}}/local
|
||||||
|
run: cmake --install ${{github.workspace}}/build_refcnt
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Configure self (reflect)
|
||||||
|
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||||
|
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
|
||||||
|
run: cmake -B ${{github.workspace}}/build_reflect -DCMAKE_MODULE_PATH=${{github.workspace}}/local/share/cmake -DCMAKE_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
|
||||||
|
|
||||||
|
- name: Build self (reflect)
|
||||||
|
# Build your program with the given configuration
|
||||||
|
run: cmake --build ${{github.workspace}}/build_reflect --config ${{env.BUILD_TYPE}}
|
||||||
|
|
||||||
|
- name: Test self (reflect)
|
||||||
|
working-directory: ${{github.workspace}}/build_reflect
|
||||||
|
# Execute tests defined by the CMake configuration.
|
||||||
|
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
|
||||||
|
run: ctest -C ${{env.BUILD_TYPE}}
|
||||||
8
xo-reflect/.gitignore
vendored
Normal file
8
xo-reflect/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# emacs workspace config
|
||||||
|
.projectile
|
||||||
|
# clangd working space (see emacs+lsp)
|
||||||
|
.cache
|
||||||
|
# typical cmake build directory (source-tree-nephew)
|
||||||
|
.build*
|
||||||
|
# symlink to builddir/compile_commands.json; should be set manually in dev sandbox
|
||||||
|
compile_commands.json
|
||||||
29
xo-reflect/CMakeLists.txt
Normal file
29
xo-reflect/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
# xo-reflect/CMakeLists.txt
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
|
||||||
|
project(reflect VERSION 0.1)
|
||||||
|
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
include(cmake/xo-bootstrap-macros.cmake)
|
||||||
|
|
||||||
|
xo_cxx_toplevel_options3()
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
# c++ settings
|
||||||
|
|
||||||
|
set(PROJECT_CXX_FLAGS "")
|
||||||
|
#set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2")
|
||||||
|
add_definitions(${PROJECT_CXX_FLAGS})
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
|
add_subdirectory(src/reflect)
|
||||||
|
add_subdirectory(utest)
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
# provide find_package() support
|
||||||
|
|
||||||
|
xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets)
|
||||||
|
|
||||||
|
# end CMakeLists.txt
|
||||||
1
xo-reflect/FILESYSTEM
Normal file
1
xo-reflect/FILESYSTEM
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
repo -- git submoduules here
|
||||||
55
xo-reflect/README.md
Normal file
55
xo-reflect/README.md
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
# reflection library
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
### build + install `xo-cmake` dependency
|
||||||
|
|
||||||
|
- [github/Rconybea/xo-cmake](https://github.com/Rconybea/xo-cmake)
|
||||||
|
|
||||||
|
Installs a few cmake ingredients, along with a build assistant `xo-build` for XO projects such as this one.
|
||||||
|
|
||||||
|
### build + install other XO dependencies
|
||||||
|
```
|
||||||
|
$ xo-build --clone --configure --build --install xo-indentlog
|
||||||
|
$ xo-build --clone --configure --build --install xo-refnct
|
||||||
|
$ xo-build --clone --configure --build --install xo-subsys
|
||||||
|
$ xo-build --clone --configure --build --install xo-reflectutil
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: can use `-n` to dry-run here
|
||||||
|
|
||||||
|
### copy `xo-reflect` repository locally
|
||||||
|
```
|
||||||
|
$ xo-build --clone xo-reflect
|
||||||
|
```
|
||||||
|
|
||||||
|
or equivalently
|
||||||
|
```
|
||||||
|
$ git clone git@github.com:Rconybea/xo-reflect.git
|
||||||
|
```
|
||||||
|
|
||||||
|
### build + install `xo-reflect`
|
||||||
|
```
|
||||||
|
$ xo-build --configure --build --install xo-reflect
|
||||||
|
```
|
||||||
|
|
||||||
|
or equivalently:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ PREFIX=/usr/local # or wherever you prefer
|
||||||
|
$ cmake -DCMAKE_INSTALL_PREFIX=${PREFIX} -S xo-reflect -B xo-reflect/.build
|
||||||
|
$ cmake --build xo-reflect/.build
|
||||||
|
$ cmake --install xo-reflect/.build
|
||||||
|
```
|
||||||
|
|
||||||
|
### build for unit test coverage
|
||||||
|
```
|
||||||
|
$ cmake -DCMAKE_BUILD_TYPE=coverage -DCMAKE_INSTALL_PREFIX=$PREFIX xo-reflect/.build-ccov
|
||||||
|
$ cmake --build xo-reflect/.build-ccov
|
||||||
|
```
|
||||||
|
|
||||||
|
### LSP support
|
||||||
|
```
|
||||||
|
$ cd xo-reflect
|
||||||
|
$ ln -s .build/compile_commands.json # lsp will look for compile_commands.json in the root of the source tree
|
||||||
|
```
|
||||||
8
xo-reflect/cmake/reflectConfig.cmake.in
Normal file
8
xo-reflect/cmake/reflectConfig.cmake.in
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
@PACKAGE_INIT@
|
||||||
|
|
||||||
|
include(CMakeFindDependencyMacro)
|
||||||
|
find_dependency(refcnt)
|
||||||
|
find_dependency(indentlog)
|
||||||
|
find_dependency(subsys)
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
|
||||||
|
check_required_components("@PROJECT_NAME@")
|
||||||
8
xo-reflect/cmake/run-external-ctest
Executable file
8
xo-reflect/cmake/run-external-ctest
Executable file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# $1 = build directory
|
||||||
|
|
||||||
|
cd $1
|
||||||
|
shift
|
||||||
|
|
||||||
|
ctest "${@}"
|
||||||
35
xo-reflect/cmake/xo-bootstrap-macros.cmake
Normal file
35
xo-reflect/cmake/xo-bootstrap-macros.cmake
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
# for example:
|
||||||
|
# $ PREFIX=/usr/local # for example
|
||||||
|
# $ cmake -DCMAKE_MODULE_PATH=prefix -DCMAKE_INSTALL_PREFIX=$PREFIX -B .build
|
||||||
|
#
|
||||||
|
# will get
|
||||||
|
# CMAKE_MODULE_PATH
|
||||||
|
# from xo-cmake-config --cmake-module-path
|
||||||
|
#
|
||||||
|
# and expect .cmake macros in
|
||||||
|
# CMAKE_MODULE_PATH/xo_macros/xo_cxx.cmake
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
|
find_program(XO_CMAKE_CONFIG_EXECUTABLE NAMES xo-cmake-config REQUIRED)
|
||||||
|
|
||||||
|
if ("${XO_CMAKE_CONFIG_EXECUTABLE}" STREQUAL "XO_CMAKE_CONFIG_EXECUTABLE-NOT_FOUND")
|
||||||
|
message(FATAL "could not find xo-cmake-config executable")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}")
|
||||||
|
|
||||||
|
if (NOT XO_SUBMODULE_BUILD)
|
||||||
|
if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL prefix))
|
||||||
|
# default to typical install location for xo-project-macros
|
||||||
|
execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --cmake-module-path OUTPUT_VARIABLE CMAKE_MODULE_PATH)
|
||||||
|
message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# needs to have been installed somewhere on CMAKE_MODULE_PATH,
|
||||||
|
# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX)
|
||||||
|
#
|
||||||
|
include(xo_macros/xo_cxx)
|
||||||
|
|
||||||
|
xo_cxx_bootstrap_message()
|
||||||
32
xo-reflect/include/xo/reflect/CMakeLists.txt
Normal file
32
xo-reflect/include/xo/reflect/CMakeLists.txt
Normal 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
|
||||||
76
xo-reflect/include/xo/reflect/EstablishTypeDescr.hpp
Normal file
76
xo-reflect/include/xo/reflect/EstablishTypeDescr.hpp
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
/* file EstablishTypeDescr.hpp
|
||||||
|
*
|
||||||
|
* author: Roland Conybeare, Aug 2022
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "TypeDescr.hpp"
|
||||||
|
#include "TaggedPtr.hpp"
|
||||||
|
|
||||||
|
namespace xo {
|
||||||
|
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); }
|
||||||
|
#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),
|
||||||
|
std::string(type_name<T>()),
|
||||||
|
nullptr /*tdextra*/,
|
||||||
|
nullptr /*invoker*/);
|
||||||
|
|
||||||
|
#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 */
|
||||||
41
xo-reflect/include/xo/reflect/Metatype.hpp
Normal file
41
xo-reflect/include/xo/reflect/Metatype.hpp
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
/* @file Metatype.hpp */
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace xo {
|
||||||
|
namespace reflect {
|
||||||
|
enum class Metatype { mt_invalid, mt_atomic, mt_pointer, mt_vector, mt_struct, mt_function };
|
||||||
|
|
||||||
|
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;
|
||||||
|
case Metatype::mt_function:
|
||||||
|
os << "function";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
os << "???";
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
} /*operator<<*/
|
||||||
|
|
||||||
|
} /*namespace reflect*/
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
/* end Metatype.hpp */
|
||||||
42
xo-reflect/include/xo/reflect/Object.hpp
Normal file
42
xo-reflect/include/xo/reflect/Object.hpp
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
/** @file Object.hpp
|
||||||
|
*
|
||||||
|
* Author: Roland Conybeare
|
||||||
|
**/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "xo/reflect/SelfTagging.hpp"
|
||||||
|
//#include <cstdint>
|
||||||
|
|
||||||
|
namespace xo {
|
||||||
|
namespace reflect {
|
||||||
|
/** @class Object
|
||||||
|
*
|
||||||
|
* @brief A swiss-army-knife base class for runtime polymorphism.
|
||||||
|
*
|
||||||
|
* Promote using this:
|
||||||
|
* - for interpreter integration (see xo-expression / xo-jit)
|
||||||
|
* - to allow reasonably efficient type dispatching -
|
||||||
|
* don't need to pay for a function call to find out dispatching type.
|
||||||
|
**/
|
||||||
|
class Object : public reflect::SelfTagging {
|
||||||
|
public:
|
||||||
|
Object(TypeId type_id) : type_id_{type_id} {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/** unique id number for this object's type
|
||||||
|
*
|
||||||
|
* Caches the value of this->self_tp().td()->id()
|
||||||
|
*
|
||||||
|
* Notes:
|
||||||
|
* 1. may want to record metatype also
|
||||||
|
* 2. a few builtin types have well-known type_ids.
|
||||||
|
* see TypeDescrTable ctor in xo-reflect.
|
||||||
|
**/
|
||||||
|
TypeId type_id_;
|
||||||
|
};
|
||||||
|
} /*namespace obj*/
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
|
||||||
|
/** end Object.hpp **/
|
||||||
424
xo-reflect/include/xo/reflect/Reflect.hpp
Normal file
424
xo-reflect/include/xo/reflect/Reflect.hpp
Normal file
|
|
@ -0,0 +1,424 @@
|
||||||
|
/* 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 <vector>
|
||||||
|
#include <array>
|
||||||
|
#include <utility> // for std::pair<>
|
||||||
|
|
||||||
|
namespace xo {
|
||||||
|
namespace reflect {
|
||||||
|
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(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----- xo::ref::rp<Object> -----
|
||||||
|
|
||||||
|
template<typename Object>
|
||||||
|
class EstablishTdx<rp<Object>> {
|
||||||
|
public:
|
||||||
|
/* definition provide after decl for Reflect {} below */
|
||||||
|
static std::unique_ptr<TypeDescrExtra> make();
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----- std::array<Element, N> -----
|
||||||
|
|
||||||
|
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();
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----- std::vector<Element> -----
|
||||||
|
|
||||||
|
template<typename Element>
|
||||||
|
class EstablishTdx<std::vector<Element>> {
|
||||||
|
public:
|
||||||
|
/* definition provide after decl for Reflect {} below */
|
||||||
|
static std::unique_ptr<TypeDescrExtra> make();
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----- std::pair<Lhs, Rhs> -----
|
||||||
|
|
||||||
|
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();
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----- 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 (*)(A1 .. An) noexcept -----
|
||||||
|
|
||||||
|
template<typename Retval, typename... Args>
|
||||||
|
class EstablishTdx<Retval (*)(Args...) noexcept> {
|
||||||
|
public:
|
||||||
|
/* definition provided after decl for Reflect {} below */
|
||||||
|
static std::unique_ptr<TypeDescrExtra> make();
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----- EstasblishFunctionTdx -------------------------------------
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class EstablishFunctionTdx;
|
||||||
|
|
||||||
|
// ----- Retval (*)(A1 .. An) -----
|
||||||
|
|
||||||
|
template<typename Retval, typename... Args>
|
||||||
|
class EstablishFunctionTdx<Retval (*)(Args...)> {
|
||||||
|
public:
|
||||||
|
/* definition provided after decl for Reflect {} below */
|
||||||
|
static std::unique_ptr<TypeDescrExtra> make();
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Retval, typename... Args>
|
||||||
|
class EstablishFunctionTdx<Retval (*)(Args...) noexcept> {
|
||||||
|
public:
|
||||||
|
/* definition provided after decl for Reflect {} below */
|
||||||
|
static std::unique_ptr<TypeDescrExtra> make();
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----- 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(Reflect::get_final_invoker<T>(),
|
||||||
|
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<T>(),
|
||||||
|
* but will not fallback to AtomicTdx if T is not recognized as a function pointer
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
static TypeDescrW require_function() {
|
||||||
|
//static_assert(std::is_function_v<T>);
|
||||||
|
|
||||||
|
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 = EstablishFunctionTdx<T>::make();
|
||||||
|
|
||||||
|
retval_td->assign_tdextra(Reflect::get_final_invoker<T>(),
|
||||||
|
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 <typename T>
|
||||||
|
static bool is_native(TypeDescr src_td) {
|
||||||
|
return (require<T>() == 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 <typename T>
|
||||||
|
static T * recover_native(TypeDescr src_td, void * src_address) {
|
||||||
|
return TaggedPtr(src_td, src_address).recover_native<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static detail::InvokerAux<T> * get_final_invoker() {
|
||||||
|
static detail::InvokerAux<T> s_final_invoker;
|
||||||
|
|
||||||
|
return &s_final_invoker;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; /*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<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<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*/
|
||||||
|
|
||||||
|
// ----- 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*/
|
||||||
|
|
||||||
|
template <typename Retval, typename... Args>
|
||||||
|
std::unique_ptr<TypeDescrExtra>
|
||||||
|
EstablishFunctionTdx<Retval (*)(Args...)>::make() {
|
||||||
|
std::vector<TypeDescr> argv;
|
||||||
|
detail::AssembleArgv<Args...>::append_argv(&argv);
|
||||||
|
|
||||||
|
return FunctionTdx::make_function(Reflect::require<Retval>(),
|
||||||
|
std::move(argv),
|
||||||
|
false /*!is_noexcept*/);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Retval, typename... Args>
|
||||||
|
std::unique_ptr<TypeDescrExtra>
|
||||||
|
EstablishFunctionTdx<Retval (*)(Args...) noexcept>::make() {
|
||||||
|
std::vector<TypeDescr> argv;
|
||||||
|
detail::AssembleArgv<Args...>::append_argv(&argv);
|
||||||
|
|
||||||
|
return FunctionTdx::make_function(Reflect::require<Retval>(),
|
||||||
|
std::move(argv),
|
||||||
|
true /*is_noexcept*/);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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),
|
||||||
|
false /*!is_noexcept*/);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* declared above before
|
||||||
|
* class Reflect { ... }
|
||||||
|
*/
|
||||||
|
template <typename Retval, typename... Args>
|
||||||
|
std::unique_ptr<TypeDescrExtra>
|
||||||
|
EstablishTdx<Retval (*)(Args...) noexcept>::make() {
|
||||||
|
std::vector<TypeDescr> argv;
|
||||||
|
detail::AssembleArgv<Args...>::append_argv(&argv);
|
||||||
|
|
||||||
|
return FunctionTdx::make_function(Reflect::require<Retval>(),
|
||||||
|
std::move(argv),
|
||||||
|
true /*is_noexcept*/);
|
||||||
|
}
|
||||||
|
|
||||||
|
} /*namespace reflect*/
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
/* end Reflect.hpp */
|
||||||
31
xo-reflect/include/xo/reflect/SelfTagging.hpp
Normal file
31
xo-reflect/include/xo/reflect/SelfTagging.hpp
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
/* file SelfTagging.hpp
|
||||||
|
*
|
||||||
|
* author: Roland Conybeare, Aug 2022
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "xo/refcnt/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 */
|
||||||
165
xo-reflect/include/xo/reflect/StructReflector.hpp
Normal file
165
xo-reflect/include/xo/reflect/StructReflector.hpp
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
/* @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; }
|
||||||
|
TypeDescr td() const { return td_; }
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
static detail::InvokerAux<StructT> s_final_invoker;
|
||||||
|
|
||||||
|
auto tdx = StructTdx::make(std::move(this->member_v_),
|
||||||
|
have_to_self_tp,
|
||||||
|
to_self_tp_fn);
|
||||||
|
|
||||||
|
this->td_->assign_tdextra(&s_final_invoker,
|
||||||
|
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*/
|
||||||
128
xo-reflect/include/xo/reflect/TaggedPtr.hpp
Normal file
128
xo-reflect/include/xo/reflect/TaggedPtr.hpp
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
/* @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_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
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
T * recover_native() const { return this->td_->recover_native2<T>(this->td_, this->address_); }
|
||||||
|
|
||||||
|
uint32_t n_child() const {
|
||||||
|
return this->td_->n_child(this->address_);
|
||||||
|
}
|
||||||
|
|
||||||
|
TaggedPtr get_child(uint32_t i) const {
|
||||||
|
return this->td_->child_tp(i, this->address_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
|
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 */
|
||||||
88
xo-reflect/include/xo/reflect/TaggedRcptr.hpp
Normal file
88
xo-reflect/include/xo/reflect/TaggedRcptr.hpp
Normal 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 "xo/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 */
|
||||||
571
xo-reflect/include/xo/reflect/TypeDescr.hpp
Normal file
571
xo-reflect/include/xo/reflect/TypeDescr.hpp
Normal file
|
|
@ -0,0 +1,571 @@
|
||||||
|
/* @file TypeDescr.hpp */
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "TypeDescrExtra.hpp"
|
||||||
|
#include "xo/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 detail {
|
||||||
|
struct Invoker {
|
||||||
|
virtual void dtor(void * addr) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Auxiliary template for capturing destructor for type T,
|
||||||
|
* if it has one.
|
||||||
|
*
|
||||||
|
* Example
|
||||||
|
* T * p = new T(...);
|
||||||
|
* DestructorAux<T>::dtor(p);
|
||||||
|
**/
|
||||||
|
template <typename T>
|
||||||
|
struct InvokerAux : public Invoker {
|
||||||
|
virtual void dtor(void * addr) const override {
|
||||||
|
T * obj = static_cast<T *>(addr);
|
||||||
|
|
||||||
|
obj->~T();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct InvokerAux<void> : public Invoker {
|
||||||
|
virtual void dtor(void *) const override {}
|
||||||
|
};
|
||||||
|
} /*namespace detail*/
|
||||||
|
} /*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
|
||||||
|
|
||||||
|
/* hashable contents of a FunctionTdx instance (without requiring decl of TypeDescrExtra),
|
||||||
|
* for unique-ification of manually-constructed function types
|
||||||
|
*/
|
||||||
|
class FunctionTdxInfo {
|
||||||
|
public:
|
||||||
|
FunctionTdxInfo() = default;
|
||||||
|
FunctionTdxInfo(TypeDescr retval_td,
|
||||||
|
const std::vector<TypeDescr> & arg_td_v,
|
||||||
|
bool is_noexcept)
|
||||||
|
: retval_td_{retval_td},
|
||||||
|
arg_td_v_{arg_td_v},
|
||||||
|
is_noexcept_{is_noexcept}
|
||||||
|
{}
|
||||||
|
|
||||||
|
/** compare two FunctionTdxInfo objects for equality
|
||||||
|
**/
|
||||||
|
inline bool operator==(const FunctionTdxInfo & other) const noexcept {
|
||||||
|
if (retval_td_ != other.retval_td_)
|
||||||
|
return true;
|
||||||
|
if (arg_td_v_.size() != other.arg_td_v_.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (std::size_t i = 0, n = arg_td_v_.size(); i < n; ++i) {
|
||||||
|
if (arg_td_v_[i] != other.arg_td_v_[i])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_noexcept_ != other.is_noexcept_)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** construct canonical description for this type
|
||||||
|
* will be like
|
||||||
|
* Retval(*)(Arg1,..,Argn)
|
||||||
|
**/
|
||||||
|
std::string make_canonical_name() const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** function return value **/
|
||||||
|
TypeDescr retval_td_ = nullptr;
|
||||||
|
/** function arguments, in positional order **/
|
||||||
|
std::vector<TypeDescr> arg_td_v_;
|
||||||
|
/** true iff function promises never to throw **/
|
||||||
|
bool is_noexcept_ = false;
|
||||||
|
}; /*FunctionTdxInfo*/
|
||||||
|
|
||||||
|
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_native_type_table_map.find(TypeInfoRef(tinfo))
|
||||||
|
!= s_native_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(const std::type_info * tinfo,
|
||||||
|
const std::string & canonical_name,
|
||||||
|
detail::Invoker * invoker,
|
||||||
|
std::unique_ptr<TypeDescrExtra> tdextra);
|
||||||
|
|
||||||
|
/** Create type-description for function from input ingredients. **/
|
||||||
|
static TypeDescrW require_by_fn_info(const FunctionTdxInfo & fn_info);
|
||||||
|
|
||||||
|
/** lookup type by canonical name **/
|
||||||
|
static TypeDescr lookup_by_name(const std::string & canonical_name);
|
||||||
|
|
||||||
|
/** print table of reflected types to os **/
|
||||||
|
static void print_reflected_types(std::ostream & os);
|
||||||
|
/** print table of function types to os **/
|
||||||
|
static void print_function_types(std::ostream & os);
|
||||||
|
|
||||||
|
TypeId id() const { return id_; }
|
||||||
|
const std::type_info * native_typeinfo() const { return native_typeinfo_; }
|
||||||
|
const std::string & canonical_name() const { return canonical_name_; }
|
||||||
|
const std::string_view & 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
|
||||||
|
* 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<typename T>
|
||||||
|
[[deprecated]]
|
||||||
|
bool is_native() const {
|
||||||
|
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<T1>(),
|
||||||
|
* then .canonical_name is computed by type_name<T>()
|
||||||
|
*
|
||||||
|
* (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.
|
||||||
|
*
|
||||||
|
* 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<T>(). Application code should prefer any of:
|
||||||
|
* 1. recover_native2(src_td, src_address)
|
||||||
|
* 2. Reflect::recover_native<T>(src_td, src_address)
|
||||||
|
* 3. TaggedPtr(src_td,src_address).recover_native<T>()
|
||||||
|
* instead of src_td->recover_native<T>()
|
||||||
|
*
|
||||||
|
* (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<typename T>
|
||||||
|
[[deprecated]]
|
||||||
|
T * recover_native(void * address) const {
|
||||||
|
if (this->is_native<T>()) {
|
||||||
|
return reinterpret_cast<T *>(address);
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
} /*recover_native*/
|
||||||
|
|
||||||
|
/** safe downcast -- like dynamic_cast<>, but does not require a source type.
|
||||||
|
*
|
||||||
|
* Application code should prefer TaggedPtr::recover_native<T>()
|
||||||
|
*
|
||||||
|
* TODO: need variation on this to correctly-handle function types,
|
||||||
|
* since for exampple cast from void* -> void (*)() is not allowed
|
||||||
|
**/
|
||||||
|
template<typename T>
|
||||||
|
T * recover_native2(TypeDescr address_td, void * address) const {
|
||||||
|
if (this == address_td) {
|
||||||
|
return reinterpret_cast<T *>(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(); }
|
||||||
|
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.
|
||||||
|
* 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); }
|
||||||
|
/** number of children, if that number is fixed at compile time. otherwise 0
|
||||||
|
**/
|
||||||
|
uint32_t n_child_fixed() const { return this->tdextra_->n_child_fixed(); }
|
||||||
|
/** TypeDescr for i'th child, using only information available at compile time.
|
||||||
|
* e.g. for vectors/pointers, always returns ElementType.
|
||||||
|
**/
|
||||||
|
TypeDescr fixed_child_td(uint32_t i) const { return this->tdextra_->fixed_child_td(i); }
|
||||||
|
/** TaggedPtr to child @p i.
|
||||||
|
* Will report most-derived-type for type tag,
|
||||||
|
* so may refer to a proper subtype (e.g. derived class) of the type
|
||||||
|
* reported by @c fixed_child_td(i)
|
||||||
|
**/
|
||||||
|
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*/
|
||||||
|
|
||||||
|
/** nullptr for non-function types **/
|
||||||
|
const FunctionTdxInfo * fn_info() const { return this->tdextra_->fn_info(); }
|
||||||
|
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); }
|
||||||
|
bool fn_is_noexcept() const { return this->tdextra_->fn_is_noexcept(); }
|
||||||
|
|
||||||
|
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(detail::Invoker * invoker,
|
||||||
|
std::unique_ptr<TypeDescrExtra> tdx) {
|
||||||
|
this->complete_flag_ = true;
|
||||||
|
this->invoker_ = invoker;
|
||||||
|
this->tdextra_ = std::move(tdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- actions -----
|
||||||
|
|
||||||
|
private:
|
||||||
|
TypeDescrBase(TypeId id,
|
||||||
|
const std::type_info * tinfo,
|
||||||
|
const std::string & canonical_name,
|
||||||
|
std::unique_ptr<TypeDescrExtra> tdextra,
|
||||||
|
detail::Invoker * invoker);
|
||||||
|
|
||||||
|
void assign_native_tinfo(const std::type_info * tinfo) {
|
||||||
|
assert(!native_typeinfo_);
|
||||||
|
native_typeinfo_ = tinfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* invariant:
|
||||||
|
* - for all TypeDescrImpl instances x:
|
||||||
|
* - s_type_table_v[x->id()] = x
|
||||||
|
* - s_native_type_table_map[TypeInfoRef(x->typeinfo())] = x
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** vector of all TypeDescr instances, indexed by TypeId. singleton. **/
|
||||||
|
static std::vector<std::unique_ptr<TypeDescrBase>> s_type_table_v;
|
||||||
|
|
||||||
|
/** hashmap of all TypeDescr instances,
|
||||||
|
* indexed by canonical_name.
|
||||||
|
*
|
||||||
|
* For manually-constructed TypeDescr instances
|
||||||
|
* (see xo-expression for use-case) we require:
|
||||||
|
*
|
||||||
|
* - TypeDescr::canonical_name uniquely identifies type
|
||||||
|
* - to interact with an actually-equivalent type T
|
||||||
|
* constructed by c++ compiler, we need
|
||||||
|
* to use the same canonical name that the compiler uses.
|
||||||
|
*
|
||||||
|
* See type xo::reflect::type_name<>() [in demangle.hpp under xo-refcnt]
|
||||||
|
* for implementation
|
||||||
|
**/
|
||||||
|
static std::unordered_map<std::string, TypeDescrBase*> s_canonical_type_table_map;
|
||||||
|
|
||||||
|
/** hashmap of all native TypeDescr instances,
|
||||||
|
* indexed by typeinfo. singleton.
|
||||||
|
**/
|
||||||
|
static std::unordered_map<TypeInfoRef, TypeDescrBase *> s_native_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;
|
||||||
|
|
||||||
|
/** map from a vector of TypeDescr objects:
|
||||||
|
* [Retval, Arg1, ...Argn]
|
||||||
|
* to TypeDescr for function type
|
||||||
|
* Retval(*)(Arg1..Argn)
|
||||||
|
*
|
||||||
|
* Use these to unique-ify function types across:
|
||||||
|
* - types sourced natively from c++ compiler
|
||||||
|
* - types manually constructed (e.g. see Lambda.cpp in xo-expression)
|
||||||
|
**/
|
||||||
|
static std::unordered_map<FunctionTdxInfo, TypeDescrBase *> s_function_type_map;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/** unique id# for this type **/
|
||||||
|
TypeId id_;
|
||||||
|
|
||||||
|
/** typeinfo for type T, if available. nullptr otherwise.
|
||||||
|
*
|
||||||
|
* 1. Always available for type-descriptions constructed via Reflect::require<T>.
|
||||||
|
* 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<T>())
|
||||||
|
* e.g.
|
||||||
|
* xo::option::Px2
|
||||||
|
*
|
||||||
|
* NOTE: if we only had to deal with types created via Reflect::reflect<T>(),
|
||||||
|
* then canonical_name could be string_view. For manually-constructed
|
||||||
|
* types, there is no compiler-generated C-string constant to reference,
|
||||||
|
* so need to use std::string here
|
||||||
|
**/
|
||||||
|
std::string canonical_name_;
|
||||||
|
|
||||||
|
/** substring .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;
|
||||||
|
|
||||||
|
/** capture basic instance-management operations for this type.
|
||||||
|
* Given an instance T x:
|
||||||
|
* - invoker_->dtor(&x) invokes T::~T()
|
||||||
|
**/
|
||||||
|
detail::Invoker * invoker_;
|
||||||
|
|
||||||
|
/** 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<<*/
|
||||||
|
|
||||||
|
/* tag to drive overload resolution */
|
||||||
|
struct reflected_types_printer {};
|
||||||
|
|
||||||
|
inline std::ostream &
|
||||||
|
operator<<(std::ostream & os, reflected_types_printer) {
|
||||||
|
TypeDescrBase::print_reflected_types(os);
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TypeDescrTable {
|
||||||
|
public:
|
||||||
|
TypeDescrTable * instance() { return &s_instance; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
/** initialize with builtin atomic types:
|
||||||
|
* float, double, char, short, int, long, bool
|
||||||
|
**/
|
||||||
|
TypeDescrTable();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static TypeDescrTable s_instance;
|
||||||
|
};
|
||||||
|
} /*namespace reflect*/
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
/** @brief overload for hashing xo::reflect::FunctionTdxInfo objects
|
||||||
|
**/
|
||||||
|
template <>
|
||||||
|
struct hash<xo::reflect::FunctionTdxInfo> {
|
||||||
|
inline size_t operator()(const xo::reflect::FunctionTdxInfo & x) const noexcept {
|
||||||
|
/* we can hash on addresses, since TypeDescr objects are immutable */
|
||||||
|
std::size_t h = hash<xo::reflect::TypeDescr>{}(x.retval_td_);
|
||||||
|
|
||||||
|
for (std::size_t i = 0, n = x.arg_td_v_.size(); i < n; ++i) {
|
||||||
|
h = (h << 1) ^ hash<xo::reflect::TypeDescr>{}(x.arg_td_v_[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
h = (h << 1) ^ (x.is_noexcept_ ? 1 : 0);
|
||||||
|
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* end TypeDescr.hpp */
|
||||||
91
xo-reflect/include/xo/reflect/TypeDescrExtra.hpp
Normal file
91
xo-reflect/include/xo/reflect/TypeDescrExtra.hpp
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
/* @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 FunctionTdxInfo;
|
||||||
|
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_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;
|
||||||
|
/** number of children, fixed at compile time.
|
||||||
|
* Will return 0 for types like std::vector<..> (because number is unknown);
|
||||||
|
* Will also return 0 for types like {bool, int, long} (because number is zero)
|
||||||
|
**/
|
||||||
|
virtual uint32_t n_child_fixed() const = 0;
|
||||||
|
/** type description for i'th child, based on information available at compile time.
|
||||||
|
* For vectors/pointers, this always refers to element type.
|
||||||
|
*
|
||||||
|
* nullptr for atomics
|
||||||
|
**/
|
||||||
|
virtual const TypeDescrBase * fixed_child_td(uint32_t i) 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 const FunctionTdxInfo * fn_info() const { return nullptr; }
|
||||||
|
virtual const TypeDescrBase * fn_retval() const { return nullptr; }
|
||||||
|
virtual uint32_t n_fn_arg() const { return 0; }
|
||||||
|
virtual const TypeDescrBase * fn_arg(uint32_t /*i_arg*/) const { return nullptr; }
|
||||||
|
virtual bool fn_is_noexcept() const { return false; }
|
||||||
|
}; /*TypeDescrExtra*/
|
||||||
|
} /*namespace reflect*/
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
/* end TypeDescrExtra.hpp */
|
||||||
47
xo-reflect/include/xo/reflect/TypeDrivenMap.hpp
Normal file
47
xo-reflect/include/xo/reflect/TypeDrivenMap.hpp
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
/* @file TypeDrivenMap.hpp
|
||||||
|
*
|
||||||
|
* author: Roland Conybeare, Aug 2022
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "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 */
|
||||||
39
xo-reflect/include/xo/reflect/atomic/AtomicTdx.hpp
Normal file
39
xo-reflect/include/xo/reflect/atomic/AtomicTdx.hpp
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
/* @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 uint32_t n_child_fixed() const override { return 0; }
|
||||||
|
virtual TaggedPtr child_tp(uint32_t /*i*/, void * /*object*/) const override;
|
||||||
|
virtual const TypeDescrBase * fixed_child_td(uint32_t /*i*/) 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 */
|
||||||
58
xo-reflect/include/xo/reflect/function/FunctionTdx.hpp
Normal file
58
xo-reflect/include/xo/reflect/function/FunctionTdx.hpp
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
/** @file FunctionTdx.hpp
|
||||||
|
*
|
||||||
|
* Author: Roland Conybeare
|
||||||
|
**/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "xo/reflect/TypeDescrExtra.hpp"
|
||||||
|
#include "xo/reflect/EstablishTypeDescr.hpp"
|
||||||
|
|
||||||
|
namespace xo {
|
||||||
|
namespace reflect {
|
||||||
|
/** Additional type-associated information for a function/procedure **/
|
||||||
|
class FunctionTdx : public TypeDescrExtra {
|
||||||
|
public:
|
||||||
|
virtual ~FunctionTdx() = default;
|
||||||
|
|
||||||
|
/** create instance. Will be invoked exactly once for each reflected function type
|
||||||
|
*
|
||||||
|
* @param retval_td. type description for return value
|
||||||
|
* @param arg_td_v. type descriptions for arguments, in positional order
|
||||||
|
* @param is_noexcept. true iff function marked noexcept
|
||||||
|
**/
|
||||||
|
static std::unique_ptr<FunctionTdx> make_function(TypeDescr retval_td,
|
||||||
|
std::vector<TypeDescr> arg_td_v,
|
||||||
|
bool is_noexcept);
|
||||||
|
/** create instance from FunctionTdxInfo
|
||||||
|
* @param fn_info. function ingredients -- return type, arg types, noexcept
|
||||||
|
**/
|
||||||
|
static std::unique_ptr<FunctionTdx> make_function(const FunctionTdxInfo & fn_info);
|
||||||
|
|
||||||
|
// ----- Inherited from TypeDescrExtra -----
|
||||||
|
|
||||||
|
virtual Metatype metatype() const override { return Metatype::mt_function; }
|
||||||
|
virtual uint32_t n_child(void * /*object*/) const override { return 0; }
|
||||||
|
virtual uint32_t n_child_fixed() const override { return 0; }
|
||||||
|
virtual TaggedPtr child_tp(uint32_t i, void * object) const override;
|
||||||
|
virtual TypeDescr fixed_child_td(uint32_t i) const override;
|
||||||
|
const std::string & struct_member_name(uint32_t i) const override;
|
||||||
|
|
||||||
|
virtual const FunctionTdxInfo * fn_info() const override { return &info_; }
|
||||||
|
virtual TypeDescr fn_retval() const override { return info_.retval_td_; }
|
||||||
|
virtual uint32_t n_fn_arg() const override { return info_.arg_td_v_.size(); }
|
||||||
|
virtual TypeDescr fn_arg(uint32_t i) const override { return info_.arg_td_v_[i]; }
|
||||||
|
virtual bool fn_is_noexcept() const override { return info_.is_noexcept_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
FunctionTdx(const FunctionTdxInfo & fn_info);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/** ingredients in complete function type description **/
|
||||||
|
FunctionTdxInfo info_;
|
||||||
|
}; /*FunctionTdx*/
|
||||||
|
} /*namespace reflect*/
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
|
||||||
|
/** end FunctionTdx.hpp **/
|
||||||
22
xo-reflect/include/xo/reflect/init_reflect.hpp
Normal file
22
xo-reflect/include/xo/reflect/init_reflect.hpp
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
/* file init_reflect.hpp
|
||||||
|
*
|
||||||
|
* author: Roland Conybeare, Sep 2022
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "xo/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 */
|
||||||
85
xo-reflect/include/xo/reflect/pointer/PointerTdx.hpp
Normal file
85
xo-reflect/include/xo/reflect/pointer/PointerTdx.hpp
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
/* file PointerTdx.hpp
|
||||||
|
*
|
||||||
|
* author: Roland Conybeare, Sep 2022
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "xo/reflect/TypeDescrExtra.hpp"
|
||||||
|
#include "xo/reflect/EstablishTypeDescr.hpp"
|
||||||
|
#include "xo/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;
|
||||||
|
|
||||||
|
/* number of children unknown at compile time.
|
||||||
|
* null-pointer -> 0, non-null pointer -> 1
|
||||||
|
*/
|
||||||
|
virtual uint32_t n_child_fixed() const override { return 0; /*unknown*/ }
|
||||||
|
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 -----
|
||||||
|
|
||||||
|
/* Pointer = 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 TypeDescrBase * fixed_child_td(uint32_t /*i*/) const override {
|
||||||
|
return EstablishTypeDescr::establish<typename Pointer::element_type>();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 */
|
||||||
71
xo-reflect/include/xo/reflect/reflect_struct.hpp
Normal file
71
xo-reflect/include/xo/reflect/reflect_struct.hpp
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
/** @file reflect_struct.hpp
|
||||||
|
*
|
||||||
|
* Author: Roland Conybeare
|
||||||
|
**/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "StructReflector.hpp"
|
||||||
|
#include "xo/reflectutil/reflect_struct_info.hpp"
|
||||||
|
|
||||||
|
namespace xo {
|
||||||
|
namespace reflect {
|
||||||
|
namespace detail {
|
||||||
|
/**
|
||||||
|
* @pre reflect_struct_member<T,MemberIx> will separately
|
||||||
|
* have been specialized for T.
|
||||||
|
* See discussion in [reflect_struct_info.hpp]
|
||||||
|
*
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
template <typename T,
|
||||||
|
std::size_t MemberIx,
|
||||||
|
std::size_t N_Member>
|
||||||
|
struct sr_member_helper {
|
||||||
|
/** reflect members starting from member with index number @tparam MemberIx
|
||||||
|
*
|
||||||
|
* @pre Members [0,..,MemberIx-1] must be already represented in @p *p_sr
|
||||||
|
**/
|
||||||
|
static void add_members_from(StructReflector<T> * p_sr) {
|
||||||
|
const auto & member_info
|
||||||
|
= reflect_struct_member<T, MemberIx>().get();
|
||||||
|
|
||||||
|
p_sr->reflect_member(member_info.member_name_.c_str(),
|
||||||
|
member_info.member_addr_);
|
||||||
|
|
||||||
|
/** reflect remaining members **/
|
||||||
|
sr_member_helper<T, MemberIx+1, N_Member>::add_members_from(p_sr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, std::size_t MemberIx>
|
||||||
|
struct sr_member_helper<T, MemberIx, MemberIx /*N_Member*/> {
|
||||||
|
/** base case -- all members have been refleccted **/
|
||||||
|
static void add_members_from(StructReflector<T> *) {}
|
||||||
|
};
|
||||||
|
} /*namespace detail*/
|
||||||
|
|
||||||
|
|
||||||
|
/** It's awkward to have Reflect::reflect<>() do the right thing,
|
||||||
|
* because there's no way to specialize on whether a type T is a struct.
|
||||||
|
*
|
||||||
|
* Use
|
||||||
|
* xo::reflect::Reflect::reflect_struct<T>() instead
|
||||||
|
**/
|
||||||
|
template <typename T>
|
||||||
|
TypeDescr reflect_struct() {
|
||||||
|
StructReflector<T> sr;
|
||||||
|
|
||||||
|
if (sr.is_incomplete())
|
||||||
|
detail::sr_member_helper<T, 0, reflect_struct_traits<T>::n_members>::add_members_from(&sr);
|
||||||
|
|
||||||
|
/* TODO: handle composition: where T inherits another reflected type */
|
||||||
|
/* TODO: handle multiple inheritance **/
|
||||||
|
|
||||||
|
return sr.td();
|
||||||
|
}
|
||||||
|
} /*namespace reflect*/
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
|
||||||
|
/** end reflect_struct.hpp **/
|
||||||
235
xo-reflect/include/xo/reflect/struct/StructMember.hpp
Normal file
235
xo-reflect/include/xo/reflect/struct/StructMember.hpp
Normal 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 */
|
||||||
98
xo-reflect/include/xo/reflect/struct/StructTdx.hpp
Normal file
98
xo-reflect/include/xo/reflect/struct/StructTdx.hpp
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
/* @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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* object argument ignored for structs, since size is fixed */
|
||||||
|
virtual uint32_t n_child(void * /*object*/) const override { return this->member_v_.size(); }
|
||||||
|
virtual uint32_t n_child_fixed() const override { return this->member_v_.size(); }
|
||||||
|
virtual TaggedPtr child_tp(uint32_t i, void * object) const override;
|
||||||
|
virtual TypeDescr fixed_child_td(uint32_t i) 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 */
|
||||||
123
xo-reflect/include/xo/reflect/vector/VectorTdx.hpp
Normal file
123
xo-reflect/include/xo/reflect/vector/VectorTdx.hpp
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
/* 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();
|
||||||
|
|
||||||
|
/** @brief true if array elements are stored at regularly-spaced offsetts **/
|
||||||
|
virtual bool has_contiguous_storage() const = 0;
|
||||||
|
|
||||||
|
// ----- Inherited from TypeDescrExtra -----
|
||||||
|
|
||||||
|
virtual Metatype metatype() const override { return Metatype::mt_vector; }
|
||||||
|
virtual uint32_t n_child(void * object) const override = 0;
|
||||||
|
virtual uint32_t n_child_fixed() const override = 0;
|
||||||
|
virtual TaggedPtr child_tp(uint32_t i, void * object) const override = 0;
|
||||||
|
virtual TypeDescr fixed_child_td(uint32_t i) const override = 0;
|
||||||
|
/* (forbidden) */
|
||||||
|
virtual std::string const & struct_member_name(uint32_t i) const override;
|
||||||
|
}; /*VectorTdx*/
|
||||||
|
|
||||||
|
// ----- StlVectorTdx -----
|
||||||
|
|
||||||
|
/* require:
|
||||||
|
* - VectorT::value_type
|
||||||
|
* - 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 bool has_contiguous_storage() const override { return true; }
|
||||||
|
|
||||||
|
virtual uint32_t n_child(void * object) const override {
|
||||||
|
target_t * vec = reinterpret_cast<target_t *>(object);
|
||||||
|
|
||||||
|
return vec->size();
|
||||||
|
} /*n_child*/
|
||||||
|
|
||||||
|
virtual uint32_t n_child_fixed() const override { return 0; /*unknown*/ }
|
||||||
|
|
||||||
|
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*/
|
||||||
|
|
||||||
|
virtual TypeDescr fixed_child_td(uint32_t /*i*/) const override {
|
||||||
|
return EstablishTypeDescr::establish<typename VectorT::value_type>();
|
||||||
|
}
|
||||||
|
}; /*StlVectorTdx*/
|
||||||
|
|
||||||
|
// ----- std::array<Element, N> -----
|
||||||
|
|
||||||
|
/* coordinates with EstablishTdx<std::array<Element, N>>::make(),
|
||||||
|
* see [reflect/Reflect.hpp]
|
||||||
|
*/
|
||||||
|
|
||||||
|
template<typename Element, std::size_t N>
|
||||||
|
class StdArrayTdx : public StlVectorTdx<std::array<Element, N>> {
|
||||||
|
virtual uint32_t n_child(void * /*object*/) const override { return N; }
|
||||||
|
virtual uint32_t n_child_fixed() const override { return N; }
|
||||||
|
}; /*StdArrayTdx*/
|
||||||
|
|
||||||
|
// ----- 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 bool has_contiguous_storage() const override { return true; }
|
||||||
|
|
||||||
|
virtual uint32_t n_child(void * object) const override {
|
||||||
|
target_t * vec = reinterpret_cast<target_t *>(object);
|
||||||
|
|
||||||
|
return vec->size();
|
||||||
|
} /*n_child*/
|
||||||
|
|
||||||
|
virtual uint32_t n_child_fixed() const override {
|
||||||
|
return 0; /* not known without object */
|
||||||
|
}
|
||||||
|
|
||||||
|
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]));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual TypeDescr fixed_child_td(uint32_t /*i*/) const override {
|
||||||
|
return EstablishTypeDescr::establish<Element>();
|
||||||
|
}
|
||||||
|
}; /*StdVectorTdx*/
|
||||||
|
} /*namespace reflect*/
|
||||||
|
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
/* end VectorTdx.hpp */
|
||||||
19
xo-reflect/src/reflect/CMakeLists.txt
Normal file
19
xo-reflect/src/reflect/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
# reflect/CMakeLists.txt
|
||||||
|
|
||||||
|
set(SELF_LIB reflect)
|
||||||
|
set(SELF_SRCS
|
||||||
|
TypeDescr.cpp TypeDescrExtra.cpp TaggedRcptr.cpp
|
||||||
|
atomic/AtomicTdx.cpp
|
||||||
|
pointer/PointerTdx.cpp
|
||||||
|
vector/VectorTdx.cpp
|
||||||
|
struct/StructTdx.cpp struct/StructMember.cpp
|
||||||
|
function/FunctionTdx.cpp
|
||||||
|
init_reflect.cpp)
|
||||||
|
|
||||||
|
xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS})
|
||||||
|
xo_dependency(${SELF_LIB} refcnt)
|
||||||
|
xo_dependency(${SELF_LIB} indentlog)
|
||||||
|
xo_dependency(${SELF_LIB} subsys)
|
||||||
|
#xo_boost_dependency(${SELF_LIB})
|
||||||
|
|
||||||
|
# end CMakeLists.txt
|
||||||
30
xo-reflect/src/reflect/TaggedRcptr.cpp
Normal file
30
xo-reflect/src/reflect/TaggedRcptr.cpp
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
/* file TaggedRcptr.cpp
|
||||||
|
*
|
||||||
|
* author: Roland Conybeare, Aug 2022
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "TaggedRcptr.hpp"
|
||||||
|
#include "xo/indentlog/print/tag.hpp"
|
||||||
|
|
||||||
|
namespace xo {
|
||||||
|
using xo::xtag;
|
||||||
|
using xo::tostr;
|
||||||
|
|
||||||
|
namespace reflect {
|
||||||
|
void
|
||||||
|
TaggedRcptr::display(std::ostream & os) const
|
||||||
|
{
|
||||||
|
os << "<TaggedRcptr"
|
||||||
|
<< xtag("type", this->td()->canonical_name())
|
||||||
|
<< xtag("addr", this->rc_address())
|
||||||
|
<< ">";
|
||||||
|
} /*display*/
|
||||||
|
|
||||||
|
std::string
|
||||||
|
TaggedRcptr::display_string() const {
|
||||||
|
return tostr(*this);
|
||||||
|
} /*display_string*/
|
||||||
|
} /*namespace reflect*/
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
/* end TaggedRcptr.cpp */
|
||||||
330
xo-reflect/src/reflect/TypeDescr.cpp
Normal file
330
xo-reflect/src/reflect/TypeDescr.cpp
Normal file
|
|
@ -0,0 +1,330 @@
|
||||||
|
/* @file TypeDescr.cpp */
|
||||||
|
|
||||||
|
#include "TypeDescr.hpp"
|
||||||
|
#include "TaggedPtr.hpp"
|
||||||
|
#include "TypeDescrExtra.hpp"
|
||||||
|
#include "Reflect.hpp"
|
||||||
|
#include "atomic/AtomicTdx.hpp"
|
||||||
|
#include "function/FunctionTdx.hpp"
|
||||||
|
#include "xo/indentlog/scope.hpp"
|
||||||
|
|
||||||
|
namespace xo {
|
||||||
|
using xo::scope;
|
||||||
|
using xo::xtag;
|
||||||
|
using xo::tostr;
|
||||||
|
|
||||||
|
namespace reflect {
|
||||||
|
uint32_t
|
||||||
|
TypeId::s_next_id = 1;
|
||||||
|
|
||||||
|
std::string
|
||||||
|
FunctionTdxInfo::make_canonical_name() const
|
||||||
|
{
|
||||||
|
std::ostringstream ss;
|
||||||
|
|
||||||
|
ss << retval_td_->canonical_name();
|
||||||
|
ss << " (*)(";
|
||||||
|
for (std::size_t i = 0, n = arg_td_v_.size(); i < n; ++i) {
|
||||||
|
if (i > 0)
|
||||||
|
ss << ",";
|
||||||
|
ss << arg_td_v_[i]->canonical_name();
|
||||||
|
}
|
||||||
|
ss << ")";
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
} /*make_canonical_name*/
|
||||||
|
|
||||||
|
// ----- TypeDescrBase -----
|
||||||
|
|
||||||
|
std::unordered_map<FunctionTdxInfo, TypeDescrBase*>
|
||||||
|
TypeDescrBase::s_function_type_map;
|
||||||
|
|
||||||
|
std::unordered_map<std::string, TypeDescrBase*>
|
||||||
|
TypeDescrBase::s_canonical_type_table_map;
|
||||||
|
|
||||||
|
std::unordered_map<TypeInfoRef, TypeDescrBase*>
|
||||||
|
TypeDescrBase::s_native_type_table_map;
|
||||||
|
|
||||||
|
std::unordered_map<TypeInfoRef, TypeDescrBase*>
|
||||||
|
TypeDescrBase::s_coalesced_type_table_map;
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<TypeDescrBase>>
|
||||||
|
TypeDescrBase::s_type_table_v;
|
||||||
|
|
||||||
|
TypeDescrW
|
||||||
|
TypeDescrBase::require(const std::type_info * native_tinfo,
|
||||||
|
const std::string & canonical_name,
|
||||||
|
detail::Invoker * invoker,
|
||||||
|
std::unique_ptr<TypeDescrExtra> tdextra)
|
||||||
|
{
|
||||||
|
if (native_tinfo) {
|
||||||
|
/* 1. lookup by tinfo hash_code in s_type_table_map
|
||||||
|
* Not available for manually-constructed type descriptions.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
auto ix = s_native_type_table_map.find(TypeInfoRef(native_tinfo));
|
||||||
|
|
||||||
|
if ((ix != s_native_type_table_map.end()) && ix->second)
|
||||||
|
return ix->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2. lookup by tinfo hash_code in s_coalesced_type_table_map */
|
||||||
|
{
|
||||||
|
auto ix = s_coalesced_type_table_map.find(TypeInfoRef(native_tinfo));
|
||||||
|
|
||||||
|
if ((ix != s_coalesced_type_table_map.end()) && ix->second)
|
||||||
|
return ix->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 3. lookup by canonical_name, before we create a new slot.
|
||||||
|
*
|
||||||
|
* Have to accept that on clang type_info objects aren't always unique (!$@#!!)
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
auto ix = s_canonical_type_table_map.find(canonical_name);
|
||||||
|
|
||||||
|
if (ix != s_canonical_type_table_map.end()) {
|
||||||
|
/** assume existing slot, with same canonical name,
|
||||||
|
* represents the same type as native_tinfo
|
||||||
|
**/
|
||||||
|
if (native_tinfo) {
|
||||||
|
auto existing_tinfo = ix->second->native_typeinfo();
|
||||||
|
|
||||||
|
/* given we have a match:
|
||||||
|
* - on existing TypeDescr
|
||||||
|
* - with same canonical name as type assoc'd with native_tinfo
|
||||||
|
* then:
|
||||||
|
* it's possible existing TypeDescr was manually constructed
|
||||||
|
* (i.e. without capturing std::type_info).
|
||||||
|
*
|
||||||
|
* With that in mind, attach that typeinfo now
|
||||||
|
*/
|
||||||
|
if (!existing_tinfo) {
|
||||||
|
ix->second->assign_native_tinfo(native_tinfo);
|
||||||
|
|
||||||
|
s_native_type_table_map[TypeInfoRef(native_tinfo)]
|
||||||
|
= ix->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existing_tinfo
|
||||||
|
&& (existing_tinfo != native_tinfo))
|
||||||
|
{
|
||||||
|
/* we have encountered distinct std::type_info objects
|
||||||
|
* that appear to represent the same type.
|
||||||
|
* (at least types with the same canonical name)
|
||||||
|
*
|
||||||
|
* We observe this happening sometimes with clang-prepared
|
||||||
|
* shared libraries; perhaps something going wrong with
|
||||||
|
* symbol coalescing.
|
||||||
|
*
|
||||||
|
* Store the dups in s_coalesced_type_table_map for future reference.
|
||||||
|
*/
|
||||||
|
auto jx = s_coalesced_type_table_map.find(TypeInfoRef(native_tinfo));
|
||||||
|
|
||||||
|
if (jx == s_coalesced_type_table_map.end())
|
||||||
|
s_coalesced_type_table_map[TypeInfoRef(native_tinfo)]
|
||||||
|
= ix->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ix->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* when control here:
|
||||||
|
* need type added to:
|
||||||
|
* - s_type_table_v
|
||||||
|
* - s_canonical_type_table_map
|
||||||
|
* - s_native_type_table_map
|
||||||
|
* - s_coalesced_type_table_map (omit, only used for dups)
|
||||||
|
* - s_function_type_map (if type represents a function)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* allocate slot for a new TypeDescr instance: */
|
||||||
|
|
||||||
|
TypeId new_td_id = TypeId::allocate();
|
||||||
|
|
||||||
|
if (s_type_table_v.size() <= new_td_id.id())
|
||||||
|
s_type_table_v.resize(new_td_id.id() + 1);
|
||||||
|
|
||||||
|
auto & new_slot = s_type_table_v[new_td_id.id()];
|
||||||
|
|
||||||
|
auto new_td = new TypeDescrBase(new_td_id,
|
||||||
|
native_tinfo,
|
||||||
|
canonical_name,
|
||||||
|
std::move(tdextra),
|
||||||
|
invoker);
|
||||||
|
|
||||||
|
new_slot.reset(new_td);
|
||||||
|
|
||||||
|
s_canonical_type_table_map[std::string(new_slot->canonical_name())] = new_td;
|
||||||
|
if (native_tinfo)
|
||||||
|
s_native_type_table_map[TypeInfoRef(native_tinfo)] = new_td;
|
||||||
|
|
||||||
|
if (new_td->tdextra() && new_td->is_function()) {
|
||||||
|
s_function_type_map[*(new_td->fn_info())] = new_td;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_slot.get();
|
||||||
|
} /*require*/
|
||||||
|
|
||||||
|
TypeDescrW
|
||||||
|
TypeDescrBase::require_by_fn_info(const FunctionTdxInfo & fn_info) {
|
||||||
|
auto ix = s_function_type_map.find(fn_info);
|
||||||
|
|
||||||
|
if (ix != s_function_type_map.end())
|
||||||
|
return ix->second;
|
||||||
|
|
||||||
|
auto fn_tdextra = FunctionTdx::make_function(fn_info);
|
||||||
|
|
||||||
|
return require(nullptr /*native_tinfo - n/avail on this path*/,
|
||||||
|
fn_info.make_canonical_name(),
|
||||||
|
nullptr /*invoker*/,
|
||||||
|
std::move(fn_tdextra));
|
||||||
|
} /*require_by_fn_info*/
|
||||||
|
|
||||||
|
TypeDescr
|
||||||
|
TypeDescrBase::lookup_by_name(const std::string & name) {
|
||||||
|
auto ix = s_canonical_type_table_map.find(name);
|
||||||
|
|
||||||
|
if (ix == s_canonical_type_table_map.end()) {
|
||||||
|
throw std::runtime_error(tostr("TypeDescrBase::lookup_by_name"
|
||||||
|
": no registered type with canonical name T",
|
||||||
|
xtag("T", name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ix->second;
|
||||||
|
} /*lookup_by_name*/
|
||||||
|
|
||||||
|
void
|
||||||
|
TypeDescrBase::print_reflected_types(std::ostream & os)
|
||||||
|
{
|
||||||
|
os << "<type_table_v[" << s_type_table_v.size() << "]:";
|
||||||
|
|
||||||
|
for (const auto & td : s_type_table_v) {
|
||||||
|
os << "\n ";
|
||||||
|
if (td) {
|
||||||
|
td->display(os);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
os << ">\n";
|
||||||
|
} /*print_reflected_types*/
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
/* readability hack:
|
||||||
|
* foo::bar::Quux ==> Quux
|
||||||
|
* but lookout for template names:
|
||||||
|
* std::pair<std::bar, std::foo> ==> pair<std::bar, std::foo>
|
||||||
|
*/
|
||||||
|
std::string_view
|
||||||
|
unqualified_name(std::string_view const & canonical_name)
|
||||||
|
{
|
||||||
|
size_t m = canonical_name.find_first_of('<');
|
||||||
|
|
||||||
|
/* skip ':', but only in range [0..m) */
|
||||||
|
size_t p = canonical_name.find_last_of(':', m);
|
||||||
|
|
||||||
|
if (p == std::string_view::npos) {
|
||||||
|
return canonical_name;
|
||||||
|
} else {
|
||||||
|
if ((canonical_name.substr(0, 9) == "std::pair")
|
||||||
|
|| (canonical_name.substr(0, 13) == "std::_1::pair"))
|
||||||
|
{
|
||||||
|
return std::string_view("pair");
|
||||||
|
} else {
|
||||||
|
return std::string_view(canonical_name.substr(p+1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} /*unqualified_name*/
|
||||||
|
} /*namespace*/
|
||||||
|
|
||||||
|
TypeDescrBase::TypeDescrBase(TypeId id,
|
||||||
|
const std::type_info * native_tinfo,
|
||||||
|
const std::string & canonical_name,
|
||||||
|
std::unique_ptr<TypeDescrExtra> tdextra,
|
||||||
|
detail::Invoker * invoker)
|
||||||
|
: id_{std::move(id)},
|
||||||
|
native_typeinfo_{native_tinfo},
|
||||||
|
canonical_name_{std::move(canonical_name)},
|
||||||
|
short_name_{unqualified_name(canonical_name_)},
|
||||||
|
invoker_{invoker},
|
||||||
|
tdextra_{std::move(tdextra)}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TaggedPtr
|
||||||
|
TypeDescrBase::most_derived_self_tp(void * object) const
|
||||||
|
{
|
||||||
|
return this->tdextra_->most_derived_self_tp(this, object);
|
||||||
|
} /*most_derived_self_tp*/
|
||||||
|
|
||||||
|
TaggedPtr
|
||||||
|
TypeDescrBase::child_tp(uint32_t i, void * object) const
|
||||||
|
{
|
||||||
|
return this->tdextra_->child_tp(i, object);
|
||||||
|
} /*child_tp*/
|
||||||
|
|
||||||
|
void
|
||||||
|
TypeDescrBase::display(std::ostream & os) const
|
||||||
|
{
|
||||||
|
os << "<TypeDescr"
|
||||||
|
<< xtag("id", id_)
|
||||||
|
<< xtag("canonical_name", canonical_name_)
|
||||||
|
<< xtag("complete", complete_flag_)
|
||||||
|
<< xtag("metatype", this->metatype())
|
||||||
|
<< ">";
|
||||||
|
} /*display*/
|
||||||
|
|
||||||
|
std::string
|
||||||
|
TypeDescrBase::display_string() const
|
||||||
|
{
|
||||||
|
return tostr(*this);
|
||||||
|
} /*display_string*/
|
||||||
|
|
||||||
|
bool
|
||||||
|
TypeDescrBase::mark_complete()
|
||||||
|
{
|
||||||
|
bool retval = this->complete_flag_;
|
||||||
|
|
||||||
|
this->complete_flag_ = true;
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
} /*mark_complete*/
|
||||||
|
|
||||||
|
#ifdef NOT_USING
|
||||||
|
void
|
||||||
|
TypeDescrBase::assign_tdextra(std::unique_ptr<TypeDescrExtra> tdx)
|
||||||
|
{
|
||||||
|
scope log(XO_ENTER0(verbose),
|
||||||
|
xtag("canonical_name", this->canonical_name()),
|
||||||
|
xtag("tdextra.old", this->tdextra_.get()),
|
||||||
|
xtag("metatype.old", (this->tdextra_
|
||||||
|
? this->tdextra_->metatype()
|
||||||
|
: Metatype::mt_invalid)),
|
||||||
|
xtag("metatype.new", tdx->metatype()));
|
||||||
|
|
||||||
|
this->complete_flag_ = true;
|
||||||
|
this->tdextra_ = std::move(tdx);
|
||||||
|
} /*assign_tdextra*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TypeDescrTable::TypeDescrTable() {
|
||||||
|
Reflect::require<bool>();
|
||||||
|
Reflect::require<char>();
|
||||||
|
Reflect::require<short>();
|
||||||
|
Reflect::require<int>();
|
||||||
|
Reflect::require<long>();
|
||||||
|
Reflect::require<long long>();
|
||||||
|
Reflect::require<float>();
|
||||||
|
Reflect::require<double>();
|
||||||
|
Reflect::require<std::string>();
|
||||||
|
} /*ctor*/
|
||||||
|
|
||||||
|
TypeDescrTable
|
||||||
|
TypeDescrTable::s_instance;
|
||||||
|
} /*namespace reflect*/
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
/* end TypeDescr.cpp */
|
||||||
37
xo-reflect/src/reflect/TypeDescrExtra.cpp
Normal file
37
xo-reflect/src/reflect/TypeDescrExtra.cpp
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
/* file TypeDescrExtra.cpp
|
||||||
|
*
|
||||||
|
* author: Roland Conybeare, Aug 2022
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "TypeDescrExtra.hpp"
|
||||||
|
#include "TypeDescr.hpp"
|
||||||
|
#include "TaggedPtr.hpp"
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace xo {
|
||||||
|
namespace reflect {
|
||||||
|
TaggedPtr
|
||||||
|
TypeDescrExtra::most_derived_self_tp(TypeDescrBase const * object_td,
|
||||||
|
void * object) const
|
||||||
|
{
|
||||||
|
return TaggedPtr(object_td, object);
|
||||||
|
} /*most_derived_self_tp*/
|
||||||
|
|
||||||
|
std::string const &
|
||||||
|
TypeDescrExtra::struct_member_name(uint32_t /*i*/) const {
|
||||||
|
assert(false);
|
||||||
|
|
||||||
|
static std::string s_null;
|
||||||
|
return s_null;
|
||||||
|
} /*struct_member_name*/
|
||||||
|
|
||||||
|
StructMember const *
|
||||||
|
TypeDescrExtra::struct_member(uint32_t /*i*/) const {
|
||||||
|
assert(false);
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
} /*struct_member*/
|
||||||
|
} /*namespace reflect*/
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
/* end TypeDescrExtra.cpp */
|
||||||
32
xo-reflect/src/reflect/atomic/AtomicTdx.cpp
Normal file
32
xo-reflect/src/reflect/atomic/AtomicTdx.cpp
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* @file AtomicTdx.cpp */
|
||||||
|
|
||||||
|
#include "atomic/AtomicTdx.hpp"
|
||||||
|
#include "TaggedPtr.hpp"
|
||||||
|
#include "TypeDescr.hpp"
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
namespace xo {
|
||||||
|
namespace reflect {
|
||||||
|
std::unique_ptr<AtomicTdx>
|
||||||
|
AtomicTdx::make() {
|
||||||
|
return std::unique_ptr<AtomicTdx>(new AtomicTdx());
|
||||||
|
} /*make*/
|
||||||
|
|
||||||
|
TaggedPtr
|
||||||
|
AtomicTdx::child_tp(uint32_t /*i*/, void * /*object*/) const {
|
||||||
|
return TaggedPtr::universal_null();
|
||||||
|
} /*child_tp*/
|
||||||
|
|
||||||
|
TypeDescr
|
||||||
|
AtomicTdx::fixed_child_td(uint32_t /*i*/) const {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string const &
|
||||||
|
AtomicTdx::struct_member_name(uint32_t i) const {
|
||||||
|
return TypeDescrExtra::struct_member_name(i);
|
||||||
|
} /*struct_member_name*/
|
||||||
|
} /*namespace reflect*/
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
/* end AtomicTdx.cpp */
|
||||||
54
xo-reflect/src/reflect/function/FunctionTdx.cpp
Normal file
54
xo-reflect/src/reflect/function/FunctionTdx.cpp
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
/* @file FunctionTdx.cpp */
|
||||||
|
|
||||||
|
#include "function/FunctionTdx.hpp"
|
||||||
|
#include "TaggedPtr.hpp"
|
||||||
|
#include "TypeDescr.hpp"
|
||||||
|
|
||||||
|
namespace xo {
|
||||||
|
namespace reflect {
|
||||||
|
/** create instance. Will be invoked exactly once for each reflected function type **/
|
||||||
|
std::unique_ptr<FunctionTdx>
|
||||||
|
FunctionTdx::make_function(TypeDescr retval_td,
|
||||||
|
std::vector<TypeDescr> arg_td_v,
|
||||||
|
bool is_noexcept)
|
||||||
|
{
|
||||||
|
return make_function(FunctionTdxInfo(retval_td,
|
||||||
|
std::move(arg_td_v),
|
||||||
|
is_noexcept));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<FunctionTdx>
|
||||||
|
FunctionTdx::make_function(const FunctionTdxInfo & fn_info)
|
||||||
|
{
|
||||||
|
return std::unique_ptr<FunctionTdx>(new FunctionTdx(fn_info));
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionTdx::FunctionTdx(const FunctionTdxInfo & fn_info)
|
||||||
|
: info_{fn_info}
|
||||||
|
{
|
||||||
|
if (!info_.retval_td_) {
|
||||||
|
throw std::runtime_error("FunctionTdx::ctor: null return type?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TaggedPtr
|
||||||
|
FunctionTdx::child_tp(uint32_t /*i*/, void * /*object*/) const
|
||||||
|
{
|
||||||
|
return TaggedPtr::universal_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeDescr
|
||||||
|
FunctionTdx::fixed_child_td(uint32_t /*i*/) const {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &
|
||||||
|
FunctionTdx::struct_member_name(uint32_t i) const
|
||||||
|
{
|
||||||
|
return TypeDescrExtra::struct_member_name(i);
|
||||||
|
}
|
||||||
|
} /*namespace reflect*/
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
|
||||||
|
/* end FunctionTdx.cpp */
|
||||||
23
xo-reflect/src/reflect/init_reflect.cpp
Normal file
23
xo-reflect/src/reflect/init_reflect.cpp
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
/* file init_reflect.cpp
|
||||||
|
*
|
||||||
|
* author: Roland Conybeare, Sep 2022
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "init_reflect.hpp"
|
||||||
|
#include "xo/subsys/Subsystem.hpp"
|
||||||
|
|
||||||
|
namespace xo {
|
||||||
|
void
|
||||||
|
InitSubsys<S_reflect_tag>::init()
|
||||||
|
{
|
||||||
|
/* placeholder -- expecting there to be non-trivial content soon */
|
||||||
|
} /*init*/
|
||||||
|
|
||||||
|
InitEvidence
|
||||||
|
InitSubsys<S_reflect_tag>::require()
|
||||||
|
{
|
||||||
|
return Subsystem::provide<S_reflect_tag>("reflect", &init);
|
||||||
|
} /*require*/
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
/* end init_reflect.cpp */
|
||||||
17
xo-reflect/src/reflect/pointer/PointerTdx.cpp
Normal file
17
xo-reflect/src/reflect/pointer/PointerTdx.cpp
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
/* file PointerTdx.cpp
|
||||||
|
*
|
||||||
|
* author: Roland Conybeare, Sep 2022
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pointer/PointerTdx.hpp"
|
||||||
|
|
||||||
|
namespace xo {
|
||||||
|
namespace reflect {
|
||||||
|
std::string const &
|
||||||
|
PointerTdx::struct_member_name(uint32_t i) const {
|
||||||
|
return TypeDescrExtra::struct_member_name(i);
|
||||||
|
} /*struct_member_name*/
|
||||||
|
} /*namespace reflect*/
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
/* end PointerTdx.cpp */
|
||||||
36
xo-reflect/src/reflect/struct/StructMember.cpp
Normal file
36
xo-reflect/src/reflect/struct/StructMember.cpp
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
/* file StructMember.cpp
|
||||||
|
*
|
||||||
|
* author: Roland Conybeare, Aug 2022
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "struct/StructMember.hpp"
|
||||||
|
#include "xo/indentlog/scope.hpp"
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace xo {
|
||||||
|
using xo::scope;
|
||||||
|
using xo::xtag;
|
||||||
|
|
||||||
|
namespace reflect {
|
||||||
|
static_assert(std::is_move_constructible_v<StructMember>);
|
||||||
|
|
||||||
|
TaggedPtr
|
||||||
|
AbstractStructMemberAccessor::member_tp(void * struct_addr) const
|
||||||
|
{
|
||||||
|
//XO_SCOPE(lscope);
|
||||||
|
|
||||||
|
TaggedPtr retval = (this
|
||||||
|
->member_td()
|
||||||
|
->most_derived_self_tp(this->address(struct_addr)));
|
||||||
|
|
||||||
|
//lscope.log(xtag("self_td", this->struct_td()->short_name()),
|
||||||
|
// xtag("member_td.declared", this->member_td()->short_name()),
|
||||||
|
// xtag("member_td.actual", retval.td()->short_name()));
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
} /*member_tp*/
|
||||||
|
|
||||||
|
} /*namespace reflect*/
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
/* end StructMember.cpp */
|
||||||
67
xo-reflect/src/reflect/struct/StructTdx.cpp
Normal file
67
xo-reflect/src/reflect/struct/StructTdx.cpp
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
/* @file StructTdx.cpp */
|
||||||
|
|
||||||
|
#include "struct/StructTdx.hpp"
|
||||||
|
#include "TypeDescr.hpp"
|
||||||
|
|
||||||
|
namespace xo {
|
||||||
|
using std::uint32_t;
|
||||||
|
|
||||||
|
namespace reflect {
|
||||||
|
std::unique_ptr<StructTdx>
|
||||||
|
StructTdx::make(std::vector<StructMember> member_v,
|
||||||
|
bool have_to_self_tp,
|
||||||
|
std::function<TaggedPtr (void*)> to_self_tp)
|
||||||
|
{
|
||||||
|
return std::unique_ptr<StructTdx>(new StructTdx(std::move(member_v),
|
||||||
|
have_to_self_tp,
|
||||||
|
std::move(to_self_tp)));
|
||||||
|
} /*make*/
|
||||||
|
|
||||||
|
TaggedPtr
|
||||||
|
StructTdx::child_tp(uint32_t i, void * object) const
|
||||||
|
{
|
||||||
|
if (i >= this->member_v_.size()) {
|
||||||
|
/* TODO: raise exception here? */
|
||||||
|
return TaggedPtr::universal_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
const StructMember & member_info = this->member_v_[i];
|
||||||
|
|
||||||
|
return member_info.get_member_tp(object);
|
||||||
|
|
||||||
|
} /*get_child*/
|
||||||
|
|
||||||
|
TypeDescr
|
||||||
|
StructTdx::fixed_child_td(uint32_t i ) const
|
||||||
|
{
|
||||||
|
if (i >= this->member_v_.size())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const StructMember & member_info = this->member_v_[i];
|
||||||
|
|
||||||
|
return member_info.get_member_td();
|
||||||
|
} /*fixed_child_td*/
|
||||||
|
|
||||||
|
std::string const &
|
||||||
|
StructTdx::struct_member_name(uint32_t i) const
|
||||||
|
{
|
||||||
|
StructMember const * sm = this->struct_member(i);
|
||||||
|
|
||||||
|
return sm->member_name();
|
||||||
|
} /*struct_member_name*/
|
||||||
|
|
||||||
|
StructMember const *
|
||||||
|
StructTdx::struct_member(uint32_t i) const
|
||||||
|
{
|
||||||
|
if (i >= this->member_v_.size()) {
|
||||||
|
/* TODO: raise exception here */
|
||||||
|
assert(false);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &(this->member_v_[i]);
|
||||||
|
} /*struct_member*/
|
||||||
|
} /*namespace reflect*/
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
/* end StructTdx.cpp */
|
||||||
20
xo-reflect/src/reflect/vector/VectorTdx.cpp
Normal file
20
xo-reflect/src/reflect/vector/VectorTdx.cpp
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
/* file VectorTdx.cpp
|
||||||
|
*
|
||||||
|
* author: Roland Conybeare, Aug 2022
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "vector/VectorTdx.hpp"
|
||||||
|
|
||||||
|
namespace xo {
|
||||||
|
namespace reflect {
|
||||||
|
std::string const &
|
||||||
|
VectorTdx::struct_member_name(uint32_t i) const {
|
||||||
|
return TypeDescrExtra::struct_member_name(i);
|
||||||
|
} /*struct_member_name*/
|
||||||
|
|
||||||
|
} /*namespace reflect*/
|
||||||
|
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
|
||||||
|
/* end VectorTdx.cpp */
|
||||||
15
xo-reflect/utest/CMakeLists.txt
Normal file
15
xo-reflect/utest/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
# build unittest reflect/utest
|
||||||
|
|
||||||
|
set(SELF_EXECUTABLE_NAME utest.reflect)
|
||||||
|
set(SELF_SOURCE_FILES
|
||||||
|
reflect_utest_main.cpp
|
||||||
|
StructReflector.test.cpp
|
||||||
|
VectorTdx.test.cpp
|
||||||
|
StructTdx.test.cpp
|
||||||
|
FunctionTdx.test.cpp)
|
||||||
|
|
||||||
|
xo_add_utest_executable(${SELF_EXECUTABLE_NAME} ${SELF_SOURCE_FILES})
|
||||||
|
xo_self_dependency(${SELF_EXECUTABLE_NAME} reflect)
|
||||||
|
xo_external_target_dependency(${SELF_EXECUTABLE_NAME} Catch2 Catch2::Catch2)
|
||||||
|
|
||||||
|
# end CMakeLists.txt
|
||||||
40
xo-reflect/utest/FunctionTdx.test.cpp
Normal file
40
xo-reflect/utest/FunctionTdx.test.cpp
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
/* @file FunctionTdx.test.cpp */
|
||||||
|
|
||||||
|
#include "xo/reflect/Reflect.hpp"
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
|
||||||
|
namespace xo {
|
||||||
|
using xo::reflect::Reflect;
|
||||||
|
using xo::reflect::TaggedPtr;
|
||||||
|
using xo::reflect::TypeDescr;
|
||||||
|
using xo::reflect::Metatype;
|
||||||
|
|
||||||
|
namespace ut {
|
||||||
|
TEST_CASE("function-reflect1", "[reflect]") {
|
||||||
|
using FunctionType = double (*)(double);
|
||||||
|
|
||||||
|
FunctionType fn = ::sqrt;
|
||||||
|
|
||||||
|
TaggedPtr tp = Reflect::make_tp(&fn);
|
||||||
|
//TypeDescr td = Reflect::require<std::vector<double>>();
|
||||||
|
|
||||||
|
REQUIRE(Reflect::is_reflected<FunctionType>() == true);
|
||||||
|
|
||||||
|
REQUIRE(tp.td()->complete_flag());
|
||||||
|
REQUIRE(tp.address() == &fn);
|
||||||
|
REQUIRE(tp.is_function());
|
||||||
|
REQUIRE(tp.is_pointer() == false);
|
||||||
|
REQUIRE(tp.is_vector() == false);
|
||||||
|
REQUIRE(tp.is_struct() == false);
|
||||||
|
REQUIRE(tp.td()->metatype() == Metatype::mt_function);
|
||||||
|
REQUIRE(tp.recover_native<double (*)(double)>() == &fn);
|
||||||
|
REQUIRE(tp.n_child() == 0); /*not a composite*/
|
||||||
|
// REQUIRE(tp.child_td(0) == ...
|
||||||
|
REQUIRE(tp.td()->fn_retval() == Reflect::require<double>());
|
||||||
|
REQUIRE(tp.n_fn_arg() == 1);
|
||||||
|
REQUIRE(tp.td()->fn_arg(0) == Reflect::require<double>());
|
||||||
|
} /*TEST_CASE(function-reflect1)*/
|
||||||
|
} /*namespace ut*/
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
/* end FunctionTdx.test.cpp */
|
||||||
142
xo-reflect/utest/StructReflector.test.cpp
Normal file
142
xo-reflect/utest/StructReflector.test.cpp
Normal file
|
|
@ -0,0 +1,142 @@
|
||||||
|
/* file StructReflector.test.cpp
|
||||||
|
*
|
||||||
|
* author: Roland Conybeare, Aug 2022
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "xo/reflect/Reflect.hpp"
|
||||||
|
#include "xo/reflect/StructReflector.hpp"
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
|
||||||
|
#define STRINGIFY(x) #x
|
||||||
|
|
||||||
|
namespace xo {
|
||||||
|
using xo::reflect::Reflect;
|
||||||
|
using xo::reflect::TaggedPtr;
|
||||||
|
using xo::reflect::StructReflector;
|
||||||
|
using xo::reflect::Reflect;
|
||||||
|
|
||||||
|
namespace ut {
|
||||||
|
namespace {
|
||||||
|
struct TestStruct0 {};
|
||||||
|
struct TestStruct1 {};
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("struct-reflect-empty", "[reflect]") {
|
||||||
|
StructReflector<TestStruct1> sr;
|
||||||
|
|
||||||
|
REQUIRE(Reflect::is_reflected<TestStruct0>() == false);
|
||||||
|
REQUIRE(Reflect::is_reflected<TestStruct1>() == true);
|
||||||
|
|
||||||
|
TestStruct0 recd0;
|
||||||
|
TaggedPtr tp = Reflect::make_tp(&recd0);
|
||||||
|
|
||||||
|
REQUIRE(tp.address() == &recd0);
|
||||||
|
REQUIRE(tp.td() == Reflect::require<TestStruct0>());
|
||||||
|
|
||||||
|
REQUIRE(tp.n_child() == 0);
|
||||||
|
|
||||||
|
REQUIRE(tp.get_child(0).is_universal_null());
|
||||||
|
REQUIRE(tp.get_child(0).td() == nullptr);
|
||||||
|
REQUIRE(tp.get_child(0).address() == nullptr);
|
||||||
|
} /*TEST_CASE(struct-reflect-empty)*/
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct TestStructS1 { int x_; };
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("struct-reflect-s1", "[reflect]") {
|
||||||
|
StructReflector<TestStructS1> sr;
|
||||||
|
|
||||||
|
REQUIRE(Reflect::is_reflected<TestStructS1>() == true);
|
||||||
|
|
||||||
|
//sr.reflect_member(STRINGIFY(x_), &decltype(sr)::struct_t::x_);
|
||||||
|
REFLECT_LITERAL_MEMBER(sr, x_);
|
||||||
|
|
||||||
|
REQUIRE(!Reflect::require<TestStructS1>()->is_struct());
|
||||||
|
|
||||||
|
sr.require_complete();
|
||||||
|
|
||||||
|
REQUIRE(Reflect::require<TestStructS1>()->is_struct());
|
||||||
|
} /*TEST_CASE(struct-reflect-s1)*/
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct TestStructS2 { int x_; };
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("struct-reflect-s2", "[reflect]") {
|
||||||
|
StructReflector<TestStructS2> sr;
|
||||||
|
|
||||||
|
REQUIRE(Reflect::is_reflected<TestStructS2>() == true);
|
||||||
|
|
||||||
|
//sr.reflect_member(STRINGIFY(x_), &decltype(sr)::struct_t::x_);
|
||||||
|
REFLECT_MEMBER(sr, x);
|
||||||
|
|
||||||
|
REQUIRE(!Reflect::require<TestStructS2>()->is_struct());
|
||||||
|
|
||||||
|
sr.require_complete();
|
||||||
|
|
||||||
|
REQUIRE(Reflect::require<TestStructS2>()->is_struct());
|
||||||
|
|
||||||
|
TestStructS2 recd1{666};
|
||||||
|
|
||||||
|
TaggedPtr tp = Reflect::make_tp(&recd1);
|
||||||
|
|
||||||
|
REQUIRE(tp.address() == &recd1);
|
||||||
|
REQUIRE(tp.td() == Reflect::require<TestStructS2>());
|
||||||
|
|
||||||
|
REQUIRE(tp.n_child() == 1);
|
||||||
|
|
||||||
|
REQUIRE(tp.get_child(0).td() == Reflect::require<int>());
|
||||||
|
REQUIRE(tp.get_child(0).address() == &(recd1.x_));
|
||||||
|
|
||||||
|
REQUIRE(tp.get_child(1).is_universal_null());
|
||||||
|
} /*TEST_CASE(struct-reflect-s2)*/
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct TestStructS3 { int x_; char y_; double z_; };
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("struct-reflect-s3", "[reflect]") {
|
||||||
|
StructReflector<TestStructS3> sr;
|
||||||
|
|
||||||
|
REQUIRE(Reflect::is_reflected<TestStructS3>() == true);
|
||||||
|
|
||||||
|
REFLECT_MEMBER(sr, x);
|
||||||
|
REFLECT_MEMBER(sr, y);
|
||||||
|
REFLECT_MEMBER(sr, z);
|
||||||
|
|
||||||
|
REQUIRE(!Reflect::require<TestStructS3>()->is_struct());
|
||||||
|
|
||||||
|
sr.require_complete();
|
||||||
|
|
||||||
|
REQUIRE(Reflect::require<TestStructS3>()->is_struct());
|
||||||
|
|
||||||
|
/* verify we can traverse reflected instances */
|
||||||
|
TestStructS3 recd1{666, 'Y', -1.234};
|
||||||
|
|
||||||
|
TaggedPtr tp = Reflect::make_tp(&recd1);
|
||||||
|
|
||||||
|
REQUIRE(tp.address() == &recd1);
|
||||||
|
REQUIRE(tp.td() == Reflect::require<TestStructS3>());
|
||||||
|
|
||||||
|
REQUIRE(tp.n_child() == 3);
|
||||||
|
|
||||||
|
REQUIRE(tp.get_child(0).td() == Reflect::require<int>());
|
||||||
|
REQUIRE(tp.get_child(0).address() == &(recd1.x_));
|
||||||
|
|
||||||
|
REQUIRE(tp.get_child(1).td() == Reflect::require<char>());
|
||||||
|
REQUIRE(tp.get_child(1).address() == &(recd1.y_));
|
||||||
|
|
||||||
|
REQUIRE(tp.get_child(2).td() == Reflect::require<double>());
|
||||||
|
REQUIRE(tp.get_child(2).address() == &(recd1.z_));
|
||||||
|
|
||||||
|
REQUIRE(tp.get_child(3).is_universal_null());
|
||||||
|
REQUIRE(tp.get_child(3).td() == nullptr);
|
||||||
|
REQUIRE(tp.get_child(3).address() == nullptr);
|
||||||
|
|
||||||
|
} /*TEST_CASE(struct-reflect-s3)*/
|
||||||
|
} /*namespace ut */
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
|
||||||
|
/* end StructReflector.test.cpp */
|
||||||
57
xo-reflect/utest/StructTdx.test.cpp
Normal file
57
xo-reflect/utest/StructTdx.test.cpp
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
/* file StructTdx.test.cpp
|
||||||
|
*
|
||||||
|
* author: Roland Conybeare, Aug 2022
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "xo/reflect/Reflect.hpp"
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
|
||||||
|
namespace xo {
|
||||||
|
using xo::reflect::Reflect;
|
||||||
|
using xo::reflect::TaggedPtr;
|
||||||
|
using xo::reflect::TypeDescr;
|
||||||
|
using xo::reflect::Metatype;
|
||||||
|
|
||||||
|
namespace ut {
|
||||||
|
TEST_CASE("std-pair-reflect", "[reflect]") {
|
||||||
|
std::pair<int, double> p;
|
||||||
|
|
||||||
|
TaggedPtr tp = Reflect::make_tp(&p);
|
||||||
|
|
||||||
|
REQUIRE(Reflect::is_reflected<std::pair<int, double>>() == true);
|
||||||
|
|
||||||
|
REQUIRE(tp.td()->complete_flag());
|
||||||
|
REQUIRE(tp.address() == &p);
|
||||||
|
REQUIRE(tp.is_struct());
|
||||||
|
REQUIRE(tp.is_vector() == false);
|
||||||
|
REQUIRE(tp.td()->metatype() == Metatype::mt_struct);
|
||||||
|
REQUIRE(tp.recover_native<std::pair<int, double>>() == &p);
|
||||||
|
REQUIRE(tp.n_child() == 2); /* struct with 2 members */
|
||||||
|
REQUIRE(tp.struct_member_name(0) == "first");
|
||||||
|
REQUIRE(tp.struct_member_name(1) == "second");
|
||||||
|
|
||||||
|
TaggedPtr tp0 = tp.get_child(0);
|
||||||
|
|
||||||
|
REQUIRE(tp0.td()->complete_flag());
|
||||||
|
REQUIRE(tp0.address() == &(p.first));
|
||||||
|
REQUIRE(!tp0.is_vector());
|
||||||
|
REQUIRE(!tp0.is_struct());
|
||||||
|
REQUIRE(tp0.td()->metatype() == Metatype::mt_atomic);
|
||||||
|
REQUIRE(tp0.recover_native<int>() == &(p.first));
|
||||||
|
REQUIRE(tp0.n_child() == 0);
|
||||||
|
|
||||||
|
TaggedPtr tp1 = tp.get_child(1);
|
||||||
|
|
||||||
|
REQUIRE(tp1.td()->complete_flag());
|
||||||
|
REQUIRE(tp1.address() == &(p.second));
|
||||||
|
REQUIRE(!tp1.is_vector());
|
||||||
|
REQUIRE(!tp1.is_struct());
|
||||||
|
REQUIRE(tp1.td()->metatype() == Metatype::mt_atomic);
|
||||||
|
REQUIRE(tp1.recover_native<double>() == &(p.second));
|
||||||
|
REQUIRE(tp1.n_child() == 0);
|
||||||
|
|
||||||
|
} /*TEST_CASE(std-pair-reflect)*/
|
||||||
|
} /*namespace ut*/
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
/* end StructTdx.test.cpp */
|
||||||
181
xo-reflect/utest/VectorTdx.test.cpp
Normal file
181
xo-reflect/utest/VectorTdx.test.cpp
Normal file
|
|
@ -0,0 +1,181 @@
|
||||||
|
/* file VectorTdx.test.cpp
|
||||||
|
*
|
||||||
|
* author: Roland Conybeare, Aug 2022
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "xo/reflect/Reflect.hpp"
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
|
||||||
|
namespace xo {
|
||||||
|
using xo::reflect::Reflect;
|
||||||
|
using xo::reflect::TaggedPtr;
|
||||||
|
using xo::reflect::TypeDescr;
|
||||||
|
using xo::reflect::Metatype;
|
||||||
|
|
||||||
|
namespace ut {
|
||||||
|
TEST_CASE("std-vector-reflect-empty", "[reflect]") {
|
||||||
|
std::vector<double> v;
|
||||||
|
|
||||||
|
TaggedPtr tp = Reflect::make_tp(&v);
|
||||||
|
//TypeDescr td = Reflect::require<std::vector<double>>();
|
||||||
|
|
||||||
|
REQUIRE(Reflect::is_reflected<std::vector<double>>() == true);
|
||||||
|
|
||||||
|
REQUIRE(tp.td()->complete_flag());
|
||||||
|
REQUIRE(tp.address() == &v);
|
||||||
|
REQUIRE(tp.is_vector());
|
||||||
|
REQUIRE(tp.is_struct() == false);
|
||||||
|
REQUIRE(tp.td()->metatype() == Metatype::mt_vector);
|
||||||
|
REQUIRE(tp.recover_native<std::vector<double>>() == &v);
|
||||||
|
REQUIRE(tp.n_child() == 0); /*since empty vector*/
|
||||||
|
// REQUIRE(tp.child_td(0) == ...
|
||||||
|
} /*TEST_CASE(std-vector-reflect-empty)*/
|
||||||
|
|
||||||
|
TEST_CASE("std-vector-reflect-one", "[reflect]") {
|
||||||
|
std::vector<double> v = { 1.123 };
|
||||||
|
|
||||||
|
TaggedPtr tp = Reflect::make_tp(&v);
|
||||||
|
|
||||||
|
REQUIRE(Reflect::is_reflected<std::vector<double>>() == true);
|
||||||
|
|
||||||
|
REQUIRE(tp.td()->complete_flag());
|
||||||
|
REQUIRE(tp.address() == &v);
|
||||||
|
REQUIRE(tp.is_vector());
|
||||||
|
REQUIRE(tp.is_struct() == false);
|
||||||
|
REQUIRE(tp.td()->metatype() == Metatype::mt_vector);
|
||||||
|
REQUIRE(tp.recover_native<std::vector<double>>() == &v);
|
||||||
|
REQUIRE(tp.n_child() == 1);
|
||||||
|
|
||||||
|
TaggedPtr tp0 = tp.get_child(0);
|
||||||
|
|
||||||
|
REQUIRE(tp0.td()->complete_flag());
|
||||||
|
REQUIRE(tp0.address() == &(v[0]));
|
||||||
|
REQUIRE(!tp0.is_vector());
|
||||||
|
REQUIRE(!tp0.is_struct());
|
||||||
|
REQUIRE(tp0.td()->metatype() == Metatype::mt_atomic);
|
||||||
|
REQUIRE(tp0.recover_native<double>() == &(v[0]));
|
||||||
|
REQUIRE(tp0.n_child() == 0);
|
||||||
|
} /*TEST_CASE(std-vector-reflect-one)*/
|
||||||
|
|
||||||
|
TEST_CASE("std-vector-reflect-two", "[reflect]") {
|
||||||
|
std::vector<double> v = { 1.123, 2.234 };
|
||||||
|
|
||||||
|
TaggedPtr tp = Reflect::make_tp(&v);
|
||||||
|
|
||||||
|
REQUIRE(Reflect::is_reflected<std::vector<double>>() == true);
|
||||||
|
|
||||||
|
REQUIRE(tp.td()->complete_flag());
|
||||||
|
REQUIRE(tp.address() == &v);
|
||||||
|
REQUIRE(tp.is_vector());
|
||||||
|
REQUIRE(tp.is_struct() == false);
|
||||||
|
REQUIRE(tp.td()->metatype() == Metatype::mt_vector);
|
||||||
|
REQUIRE(tp.recover_native<std::vector<double>>() == &v);
|
||||||
|
REQUIRE(tp.n_child() == 2);
|
||||||
|
|
||||||
|
TaggedPtr tp0 = tp.get_child(0);
|
||||||
|
|
||||||
|
REQUIRE(tp0.td()->complete_flag());
|
||||||
|
REQUIRE(tp0.address() == &(v[0]));
|
||||||
|
REQUIRE(!tp0.is_vector());
|
||||||
|
REQUIRE(!tp0.is_struct());
|
||||||
|
REQUIRE(tp0.td()->metatype() == Metatype::mt_atomic);
|
||||||
|
REQUIRE(tp0.recover_native<double>() == &(v[0]));
|
||||||
|
REQUIRE(tp0.n_child() == 0);
|
||||||
|
|
||||||
|
TaggedPtr tp1 = tp.get_child(1);
|
||||||
|
|
||||||
|
REQUIRE(tp1.td()->complete_flag());
|
||||||
|
REQUIRE(tp1.address() == &(v[1]));
|
||||||
|
REQUIRE(!tp1.is_vector());
|
||||||
|
REQUIRE(!tp1.is_struct());
|
||||||
|
REQUIRE(tp1.td()->metatype() == Metatype::mt_atomic);
|
||||||
|
REQUIRE(tp1.recover_native<double>() == &(v[1]));
|
||||||
|
REQUIRE(tp1.n_child() == 0);
|
||||||
|
} /*TEST(std-vector-reflect-two)*/
|
||||||
|
|
||||||
|
// ----- std::array -----
|
||||||
|
|
||||||
|
TEST_CASE("std-array-reflect-empty", "[reflect]") {
|
||||||
|
std::array<double, 0> v;
|
||||||
|
|
||||||
|
TaggedPtr tp = Reflect::make_tp(&v);
|
||||||
|
//TypeDescr td = Reflect::require<std::array<double, xx>>();
|
||||||
|
|
||||||
|
REQUIRE(Reflect::is_reflected<std::array<double, 0>>() == true);
|
||||||
|
|
||||||
|
REQUIRE(tp.td()->complete_flag());
|
||||||
|
REQUIRE(tp.address() == &v);
|
||||||
|
REQUIRE(tp.is_vector());
|
||||||
|
REQUIRE(tp.is_struct() == false);
|
||||||
|
REQUIRE(tp.td()->metatype() == Metatype::mt_vector);
|
||||||
|
REQUIRE(tp.recover_native<std::array<double, 0>>() == &v);
|
||||||
|
REQUIRE(tp.n_child() == 0); /*since empty vector*/
|
||||||
|
// REQUIRE(tp.child_td(0) == ...
|
||||||
|
} /*TEST_CASE(std-array-reflect-empty)*/
|
||||||
|
|
||||||
|
TEST_CASE("std-array-reflect-one", "[reflect]") {
|
||||||
|
std::array<double, 1> v = { 1.123 };
|
||||||
|
|
||||||
|
TaggedPtr tp = Reflect::make_tp(&v);
|
||||||
|
|
||||||
|
REQUIRE(Reflect::is_reflected<std::array<double, 1>>() == true);
|
||||||
|
|
||||||
|
REQUIRE(tp.td()->complete_flag());
|
||||||
|
REQUIRE(tp.address() == &v);
|
||||||
|
REQUIRE(tp.is_vector());
|
||||||
|
REQUIRE(tp.is_struct() == false);
|
||||||
|
REQUIRE(tp.td()->metatype() == Metatype::mt_vector);
|
||||||
|
REQUIRE(tp.recover_native<std::array<double, 1>>() == &v);
|
||||||
|
REQUIRE(tp.n_child() == 1);
|
||||||
|
|
||||||
|
TaggedPtr tp0 = tp.get_child(0);
|
||||||
|
|
||||||
|
REQUIRE(tp0.td()->complete_flag());
|
||||||
|
REQUIRE(tp0.address() == &(v[0]));
|
||||||
|
REQUIRE(!tp0.is_vector());
|
||||||
|
REQUIRE(!tp0.is_struct());
|
||||||
|
REQUIRE(tp0.td()->metatype() == Metatype::mt_atomic);
|
||||||
|
REQUIRE(tp0.recover_native<double>() == &(v[0]));
|
||||||
|
REQUIRE(tp0.n_child() == 0);
|
||||||
|
} /*TEST_CASE(std-array-reflect-one)*/
|
||||||
|
|
||||||
|
TEST_CASE("std-array-reflect-two", "[reflect]") {
|
||||||
|
std::array<double, 2> v = { 1.123, 2.234 };
|
||||||
|
|
||||||
|
TaggedPtr tp = Reflect::make_tp(&v);
|
||||||
|
|
||||||
|
REQUIRE(Reflect::is_reflected<std::array<double, 2>>() == true);
|
||||||
|
|
||||||
|
REQUIRE(tp.td()->complete_flag());
|
||||||
|
REQUIRE(tp.address() == &v);
|
||||||
|
REQUIRE(tp.is_vector());
|
||||||
|
REQUIRE(tp.is_struct() == false);
|
||||||
|
REQUIRE(tp.td()->metatype() == Metatype::mt_vector);
|
||||||
|
REQUIRE(tp.recover_native<std::array<double, 2>>() == &v);
|
||||||
|
REQUIRE(tp.n_child() == 2);
|
||||||
|
|
||||||
|
TaggedPtr tp0 = tp.get_child(0);
|
||||||
|
|
||||||
|
REQUIRE(tp0.td()->complete_flag());
|
||||||
|
REQUIRE(tp0.address() == &(v[0]));
|
||||||
|
REQUIRE(!tp0.is_vector());
|
||||||
|
REQUIRE(!tp0.is_struct());
|
||||||
|
REQUIRE(tp0.td()->metatype() == Metatype::mt_atomic);
|
||||||
|
REQUIRE(tp0.recover_native<double>() == &(v[0]));
|
||||||
|
REQUIRE(tp0.n_child() == 0);
|
||||||
|
|
||||||
|
TaggedPtr tp1 = tp.get_child(1);
|
||||||
|
|
||||||
|
REQUIRE(tp1.td()->complete_flag());
|
||||||
|
REQUIRE(tp1.address() == &(v[1]));
|
||||||
|
REQUIRE(!tp1.is_vector());
|
||||||
|
REQUIRE(!tp1.is_struct());
|
||||||
|
REQUIRE(tp1.td()->metatype() == Metatype::mt_atomic);
|
||||||
|
REQUIRE(tp1.recover_native<double>() == &(v[1]));
|
||||||
|
REQUIRE(tp1.n_child() == 0);
|
||||||
|
} /*TEST(std-array-reflect-two)*/
|
||||||
|
|
||||||
|
} /*namespace ut*/
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
/* end VectorTdx.test.cpp */
|
||||||
6
xo-reflect/utest/reflect_utest_main.cpp
Normal file
6
xo-reflect/utest/reflect_utest_main.cpp
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
/* file reflect_utest_main.cpp */
|
||||||
|
|
||||||
|
#define CATCH_CONFIG_MAIN
|
||||||
|
#include "catch2/catch.hpp"
|
||||||
|
|
||||||
|
/* end reflect_utest_main.cpp */
|
||||||
Loading…
Add table
Add a link
Reference in a new issue