From a29faaa1ae1bdebded9b594f7d91cf88ce147aad Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 24 Sep 2023 17:31:32 -0400 Subject: [PATCH 001/111] initial commit --- README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..27fe0373 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# reflection library From 064659e17945a63ac3753f4020dcc82c184144bb Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 24 Sep 2023 19:41:40 -0400 Subject: [PATCH 002/111] + submodule indentlog --- .gitmodules | 6 ++++++ repo/indentlog | 1 + 2 files changed, 7 insertions(+) create mode 100644 .gitmodules create mode 160000 repo/indentlog diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..698a44d5 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "repo/indentlog"] + path = repo/indentlog + url = git@github.com:Rconybea/indentlog.git +[submodule "repo/refcnt/refcnt"] + path = repo/refcnt/refcnt + url = git@github.com:Rconybea/refcnt.git diff --git a/repo/indentlog b/repo/indentlog new file mode 160000 index 00000000..bf92724a --- /dev/null +++ b/repo/indentlog @@ -0,0 +1 @@ +Subproject commit bf92724aa5d123b6acb9b00318e579828fb9ff38 From 2e63293139be22e487ef82a6f20fefcf1bdd6608 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 25 Sep 2023 17:47:13 -0400 Subject: [PATCH 003/111] + git submodules {subsys, refcnt} --- .gitmodules | 11 +++++++---- repo/refcnt | 1 + repo/subsys | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) create mode 160000 repo/refcnt create mode 160000 repo/subsys diff --git a/.gitmodules b/.gitmodules index 698a44d5..ebcf3a27 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,9 @@ [submodule "repo/indentlog"] - path = repo/indentlog - url = git@github.com:Rconybea/indentlog.git -[submodule "repo/refcnt/refcnt"] - path = repo/refcnt/refcnt + path = repo/indentlog + url = git@github.com:Rconybea/indentlog.git +[submodule "repo/refcnt"] + path = repo/refcnt url = git@github.com:Rconybea/refcnt.git +[submodule "repo/subsys"] + path = repo/subsys + url = git@github.com:Rconybea/subsys.git diff --git a/repo/refcnt b/repo/refcnt new file mode 160000 index 00000000..4b3c854f --- /dev/null +++ b/repo/refcnt @@ -0,0 +1 @@ +Subproject commit 4b3c854fc7872ba404ce4e0af25ecfdc47822ae1 diff --git a/repo/subsys b/repo/subsys new file mode 160000 index 00000000..1c384e1f --- /dev/null +++ b/repo/subsys @@ -0,0 +1 @@ +Subproject commit 1c384e1ff0a01885bdf0713be858085814b6ece6 From fdb4ca37f44105d8062f31a3d1dcffeaaf3c0efc Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 25 Sep 2023 17:49:42 -0400 Subject: [PATCH 004/111] reflect: initial implementation --- CMakeLists.txt | 184 +++++++ FILESYSTEM | 1 + cmake/code-coverage.cmake | 678 ++++++++++++++++++++++++ cmake/cxx.cmake | 98 ++++ cmake/reflectConfig.cmake.in | 4 + cmake/run-external-ctest | 8 + include/reflect/CMakeLists.txt | 32 ++ include/reflect/EstablishTypeDescr.hpp | 62 +++ include/reflect/Metatype.hpp | 38 ++ include/reflect/Reflect.hpp | 235 ++++++++ include/reflect/SelfTagging.hpp | 31 ++ include/reflect/StructReflector.hpp | 161 ++++++ include/reflect/TaggedPtr.hpp | 125 +++++ include/reflect/TaggedRcptr.hpp | 88 +++ include/reflect/TypeDescr.hpp | 302 +++++++++++ include/reflect/TypeDescrExtra.hpp | 65 +++ include/reflect/TypeDrivenMap.hpp | 47 ++ include/reflect/atomic/AtomicTdx.hpp | 37 ++ include/reflect/init_reflect.hpp | 22 + include/reflect/pointer/PointerTdx.hpp | 76 +++ include/reflect/struct/StructMember.hpp | 236 +++++++++ include/reflect/struct/StructTdx.hpp | 94 ++++ include/reflect/vector/VectorTdx.hpp | 100 ++++ repo/README.md | 5 + src/reflect/CMakeLists.txt | 52 ++ src/reflect/TaggedRcptr.cpp | 30 ++ src/reflect/TypeDescr.cpp | 194 +++++++ src/reflect/TypeDescrExtra.cpp | 37 ++ src/reflect/atomic/AtomicTdx.cpp | 24 + src/reflect/init_reflect.cpp | 23 + src/reflect/pointer/PointerTdx.cpp | 17 + src/reflect/struct/StructMember.cpp | 36 ++ src/reflect/struct/StructTdx.cpp | 55 ++ src/reflect/vector/VectorTdx.cpp | 20 + utest/CMakeLists.txt | 57 ++ utest/StructReflector.test.cpp | 142 +++++ utest/StructTdx.test.cpp | 59 +++ utest/VectorTdx.test.cpp | 181 +++++++ utest/reflect_utest_main.cpp | 6 + 39 files changed, 3662 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 FILESYSTEM create mode 100644 cmake/code-coverage.cmake create mode 100644 cmake/cxx.cmake create mode 100644 cmake/reflectConfig.cmake.in create mode 100755 cmake/run-external-ctest create mode 100644 include/reflect/CMakeLists.txt create mode 100644 include/reflect/EstablishTypeDescr.hpp create mode 100644 include/reflect/Metatype.hpp create mode 100644 include/reflect/Reflect.hpp create mode 100644 include/reflect/SelfTagging.hpp create mode 100644 include/reflect/StructReflector.hpp create mode 100644 include/reflect/TaggedPtr.hpp create mode 100644 include/reflect/TaggedRcptr.hpp create mode 100644 include/reflect/TypeDescr.hpp create mode 100644 include/reflect/TypeDescrExtra.hpp create mode 100644 include/reflect/TypeDrivenMap.hpp create mode 100644 include/reflect/atomic/AtomicTdx.hpp create mode 100644 include/reflect/init_reflect.hpp create mode 100644 include/reflect/pointer/PointerTdx.hpp create mode 100644 include/reflect/struct/StructMember.hpp create mode 100644 include/reflect/struct/StructTdx.hpp create mode 100644 include/reflect/vector/VectorTdx.hpp create mode 100644 repo/README.md create mode 100644 src/reflect/CMakeLists.txt create mode 100644 src/reflect/TaggedRcptr.cpp create mode 100644 src/reflect/TypeDescr.cpp create mode 100644 src/reflect/TypeDescrExtra.cpp create mode 100644 src/reflect/atomic/AtomicTdx.cpp create mode 100644 src/reflect/init_reflect.cpp create mode 100644 src/reflect/pointer/PointerTdx.cpp create mode 100644 src/reflect/struct/StructMember.cpp create mode 100644 src/reflect/struct/StructTdx.cpp create mode 100644 src/reflect/vector/VectorTdx.cpp create mode 100644 utest/CMakeLists.txt create mode 100644 utest/StructReflector.test.cpp create mode 100644 utest/StructTdx.test.cpp create mode 100644 utest/VectorTdx.test.cpp create mode 100644 utest/reflect_utest_main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..4f777c59 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,184 @@ +# reflect/CMakeLists.txt + +cmake_minimum_required(VERSION 3.10) + +project(reflect VERSION 0.1) +enable_language(CXX) + +include(cmake/cxx.cmake) +include(cmake/code-coverage.cmake) + +# ---------------------------------------------------------------- +# unit test setup + +enable_testing() +# activate code coverage for all executables + libraries (when configured with -DCODE_COVERAGE=ON) +add_code_coverage() +# 1. assuming that /nix/store/ prefixes .hpp files belonging to gcc, catch2 etc. +# we're not interested in code coverage for these sources. +# 2. exclude the utest/ subdir, we don't need coverage on the unit tests themselves; +# rather, want coverage on the code that the unit tests exercise. +# +# NOTE: this seems to work only with the 'ccov-all' target. In particular, doesn't seem to do anything with the 'ccov' target +# +add_code_coverage_all_targets(EXCLUDE /nix/store/* ${PROJECT_SOURCE_DIR}/utest/* ${PROJECT_BINARY_DIR}/local/* ${PROJECT_SOURCE_DIR}/repo/*) + +# ---------------------------------------------------------------- +# c++ settings + +set(XO_PROJECT_NAME reflect) +set(PROJECT_CXX_FLAGS "") +#set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2") + +add_definitions(${PROJECT_CXX_FLAGS}) + +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 20) +endif() + +set(CMAKE_CXX_STANDARD_REQUIRED True) + +# always write compile_commands.json +set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "") + +# ---------------------------------------------------------------- +# external projects (need these to exist before add_subdirectory() below) +# +# we are expecting these projects to coexist peacefully in build/local +# (i.e. can run their `make install` steps independently with prefix build/local, +# without any collisions) +# + +include(ExternalProject) + +## ----- indentlog ------ + +# NOTE: we could have cmake handle git interaction, +# but we want source for certain dependencies to live in a location +# that's suitable for accepting changes + coordinated commits. +# In particular, not in the build directory! +# +externalproject_add( + project_indentlog + SOURCE_DIR ${PROJECT_SOURCE_DIR}/repo/indentlog + BINARY_DIR ${PROJECT_BINARY_DIR}/ext/indentlog + INSTALL_DIR ${PROJECT_BINARY_DIR}/local + CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCODE_COVERAGE=${CODE_COVERAGE} -DCMAKE_PREFIX_PATH= -DCMAKE_INSTALL_PREFIX= + BUILD_COMMAND make + INSTALL_COMMAND make install + TEST_BEFORE_INSTALL True +) + +add_library(indentlog INTERFACE IMPORTED) +#set_property(TARGET indentlog PROPERTY IMPORTED_LOCATION ${PROJECT_BINARY_DIR}/local/lib/libindentlog.so) +add_dependencies(indentlog project_indentlog) + +# runs ctest in indentlog build dir +add_test(NAME indentlog COMMAND ${PROJECT_SOURCE_DIR}/cmake/run-external-ctest ${PROJECT_BINARY_DIR}/ext/indentlog) +#target_code_coverage(indentlog EXTERNAL AUTO ALL) + +# ----- subsys ----- + +externalproject_add( + project_subsys + SOURCE_DIR ${PROJECT_SOURCE_DIR}/repo/subsys + BINARY_DIR ${PROJECT_BINARY_DIR}/ext/subsys + INSTALL_DIR ${PROJECT_BINARY_DIR}/local + CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCODE_COVERAGE=${CODE_COVERAGE} -DCMAKE_PREFIX_PATH= -DCMAKE_INSTALL_PREFIX= + BUILD_COMMAND make + INSTALL_COMMAND make install + TEST_BEFORE_INSTALL True +) + +add_library(subsys INTERFACE IMPORTED) +add_dependencies(subsys project_subsys) + +# runs ctest in subsys build dir +add_test(NAME subsys COMMAND ${PROJECT_SOURCE_DIR}/cmake/run-external-ctest ${PROJECT_BINARY_DIR}/ext/subsys) + +# ----- refcnt ----- + +# CMAKE_ARGS +# CMAKE_BUILD_TYPE propagate Debug/Release build type +# CODE_COVERAGE propagate code coverage setting +# CMAKE_PREFIX_PATH path for support cmake files of dependencies (needed for find_package() to work) +# CMAKE_INSTALL_PREFIX install subproject here +# SOURCE_DIR -- where to find already established source code +# BINARY_DIR -- run build for external project here +# INSTALL_DIR -- (temporarily) install external project here +# +externalproject_add( + project_refcnt + SOURCE_DIR ${PROJECT_SOURCE_DIR}/repo/refcnt + BINARY_DIR ${PROJECT_BINARY_DIR}/ext/refcnt + INSTALL_DIR ${PROJECT_BINARY_DIR}/local + CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCODE_COVERAGE=${CODE_COVERAGE} -DCMAKE_PREFIX_PATH= -DCMAKE_INSTALL_PREFIX= + BUILD_COMMAND make + INSTALL_COMMAND make install + TEST_BEFORE_INSTALL True +) + +add_library(refcnt SHARED IMPORTED) +set_property(TARGET refcnt PROPERTY IMPORTED_LOCATION ${PROJECT_BINARY_DIR}/local/lib/librefcnt.so) +add_dependencies(refcnt project_refcnt) +add_dependencies(refcnt project_indentlog) + +# runs ctest in refcnt build dir +add_test(NAME refcnt COMMAND ${PROJECT_SOURCE_DIR}/cmake/run-external-ctest ${PROJECT_BINARY_DIR}/ext/refcnt) + +# ---------------------------------------------------------------- +# sources + +add_subdirectory(src/reflect) +add_subdirectory(utest) + +# ---------------------------------------------------------------- +# cmake export: +# +# populate .cmake files in $CMAKE_INSTALL_LIBDIR/cmake/reflect. +# cmake projects that include this directory in $CMAKE_PREFIX_PATH +# can use +# find_package(reflect REQUIRED) +# and +# target_link_libraries(${sometarget} PUBLIC reflect) +# to use the reflect library + +set(XO_PROJECT_CONFIG_VERSION "${XO_PROJECT_NAME}ConfigVersion.cmake") +set(XO_PROJECT_CONFIG "${XO_PROJECT_NAME}Config.cmake") + +include(CMakePackageConfigHelpers) + +# generates build/reflectConfigVersion.cmake +write_basic_package_version_file( + "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG_VERSION}" + VERSION 0.1 + COMPATIBILITY AnyNewerVersion +) + +# generates build/reflectConfig.cmake +configure_package_config_file( + "${PROJECT_SOURCE_DIR}/cmake/${XO_PROJECT_NAME}Config.cmake.in" + "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG}" + INSTALL_DESTINATION lib/cmake/${XO_PROJECT_NAME} +) + +# creates {reflectTargets.cmake, reflectTargets-noconfig.cmake} in $CMAKE_INSTALL_LIBDIR/cmake/reflect/ +# requires +# install(.. EXPORT reflectTargets ..) +# +install( + EXPORT ${XO_PROJECT_NAME}Targets + DESTINATION lib/cmake/${XO_PROJECT_NAME} +) + +# creates {reflectConfigVersion.cmake, reflectConfig.cmake} in $CMAKE_INSTALL_LIBDIR/cmake/reflect/ +install( + FILES + "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG_VERSION}" + "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG}" + DESTINATION lib/cmake/${XO_PROJECT_NAME}) + +# ---------------------------------------------------------------- +# install .hpp files + +install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/reflect/ DESTINATION include/reflect) diff --git a/FILESYSTEM b/FILESYSTEM new file mode 100644 index 00000000..9af86a7a --- /dev/null +++ b/FILESYSTEM @@ -0,0 +1 @@ +repo -- git submoduules here diff --git a/cmake/code-coverage.cmake b/cmake/code-coverage.cmake new file mode 100644 index 00000000..b6b36064 --- /dev/null +++ b/cmake/code-coverage.cmake @@ -0,0 +1,678 @@ +# +# Copyright (C) 2018-2020 by George Cave - gcave@stablecoder.ca +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +# USAGE: To enable any code coverage instrumentation/targets, the single CMake +# option of `CODE_COVERAGE` needs to be set to 'ON', either by GUI, ccmake, or +# on the command line. +# +# From this point, there are two primary methods for adding instrumentation to +# targets: +# +# 1 - A blanket instrumentation by calling `add_code_coverage()`, where +# all targets in that directory and all subdirectories are automatically +# instrumented. +# +# 2 - Per-target instrumentation by calling +# `target_code_coverage()`, where the target is given and thus only +# that target is instrumented. This applies to both libraries and executables. +# +# To add coverage targets, such as calling `make ccov` to generate the actual +# coverage information for perusal or consumption, call +# `target_code_coverage()` on an *executable* target. +# +# Example 1: All targets instrumented +# +# In this case, the coverage information reported will will be that of the +# `theLib` library target and `theExe` executable. +# +# 1a: Via global command +# +# ~~~ +# add_code_coverage() # Adds instrumentation to all targets +# +# add_library(theLib lib.cpp) +# +# add_executable(theExe main.cpp) +# target_link_libraries(theExe PRIVATE theLib) +# target_code_coverage(theExe) # As an executable target, adds the 'ccov-theExe' target +# # (instrumentation already added via global anyways) +# # for generating code coverage reports. +# ~~~ +# +# 1b: Via target commands +# +# ~~~ +# add_library(theLib lib.cpp) +# target_code_coverage(theLib) # As a library target, adds coverage instrumentation but no targets. +# +# add_executable(theExe main.cpp) +# target_link_libraries(theExe PRIVATE theLib) +# target_code_coverage(theExe) # As an executable target, adds the 'ccov-theExe' target and instrumentation for generating code coverage reports. +# ~~~ +# +# Example 2: Target instrumented, but with regex pattern of files to be excluded +# from report +# +# ~~~ +# add_executable(theExe main.cpp non_covered.cpp) +# target_code_coverage(theExe EXCLUDE non_covered.cpp test/*) # As an executable target, the reports will exclude the non-covered.cpp file, and any files in a test/ folder. +# ~~~ +# +# Example 3: Target added to the 'ccov' and 'ccov-all' targets +# +# ~~~ +# add_code_coverage_all_targets(EXCLUDE test/*) # Adds the 'ccov-all' target set and sets it to exclude all files in test/ folders. +# +# add_executable(theExe main.cpp non_covered.cpp) +# target_code_coverage(theExe AUTO ALL EXCLUDE non_covered.cpp test/*) # As an executable target, adds to the 'ccov' and ccov-all' targets, and the reports will exclude the non-covered.cpp file, and any files in a test/ folder. +# ~~~ + +# Options +option( + CODE_COVERAGE + "Builds targets with code coverage instrumentation. (Requires GCC or Clang)" + OFF) + +# Programs +find_program(LLVM_COV_PATH llvm-cov) +find_program(LLVM_PROFDATA_PATH llvm-profdata) +find_program(LCOV_PATH lcov) +find_program(GENHTML_PATH genhtml) +# Hide behind the 'advanced' mode flag for GUI/ccmake +mark_as_advanced(FORCE LLVM_COV_PATH LLVM_PROFDATA_PATH LCOV_PATH GENHTML_PATH) + +# Variables +set(CMAKE_COVERAGE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/ccov) +set_property(GLOBAL PROPERTY JOB_POOLS ccov_serial_pool=1) + +# Common initialization/checks +if(CODE_COVERAGE AND NOT CODE_COVERAGE_ADDED) + set(CODE_COVERAGE_ADDED ON) + + # Common Targets + add_custom_target( + ccov-preprocessing + COMMAND ${CMAKE_COMMAND} -E make_directory + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY} + DEPENDS ccov-clean) + + if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" + OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") + # Messages + message(STATUS "Building with llvm Code Coverage Tools") + + if(NOT LLVM_COV_PATH) + message(FATAL_ERROR "llvm-cov not found! Aborting.") + else() + # Version number checking for 'EXCLUDE' compatibility + execute_process(COMMAND ${LLVM_COV_PATH} --version + OUTPUT_VARIABLE LLVM_COV_VERSION_CALL_OUTPUT) + string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" LLVM_COV_VERSION + ${LLVM_COV_VERSION_CALL_OUTPUT}) + + if(LLVM_COV_VERSION VERSION_LESS "7.0.0") + message( + WARNING + "target_code_coverage()/add_code_coverage_all_targets() 'EXCLUDE' option only available on llvm-cov >= 7.0.0" + ) + endif() + endif() + + # Targets + if(${CMAKE_VERSION} VERSION_LESS "3.17.0") + add_custom_target( + ccov-clean + COMMAND ${CMAKE_COMMAND} -E remove -f + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list + COMMAND ${CMAKE_COMMAND} -E remove -f + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list) + else() + add_custom_target( + ccov-clean + COMMAND ${CMAKE_COMMAND} -E rm -f + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list + COMMAND ${CMAKE_COMMAND} -E rm -f + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list) + endif() + + # Used to get the shared object file list before doing the main all- + # processing + add_custom_target( + ccov-libs + COMMAND ; + COMMENT "libs ready for coverage report.") + + elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES + "GNU") + # Messages + message(STATUS "Building with lcov Code Coverage Tools") + + if(CMAKE_BUILD_TYPE) + string(TOUPPER ${CMAKE_BUILD_TYPE} upper_build_type) + if(NOT ${upper_build_type} STREQUAL "DEBUG") + message( + WARNING + "Code coverage results with an optimized (non-Debug) build may be misleading" + ) + endif() + else() + message( + WARNING + "Code coverage results with an optimized (non-Debug) build may be misleading" + ) + endif() + if(NOT LCOV_PATH) + message(FATAL_ERROR "lcov not found! Aborting...") + endif() + if(NOT GENHTML_PATH) + message(FATAL_ERROR "genhtml not found! Aborting...") + endif() + + # Targets + add_custom_target(ccov-clean COMMAND ${LCOV_PATH} --directory + ${CMAKE_BINARY_DIR} --zerocounters) + + else() + message(FATAL_ERROR "Code coverage requires Clang or GCC. Aborting.") + endif() +endif() + +# Adds code coverage instrumentation to a library, or instrumentation/targets +# for an executable target. +# ~~~ +# EXECUTABLE ADDED TARGETS: +# GCOV/LCOV: +# ccov : Generates HTML code coverage report for every target added with 'AUTO' parameter. +# ccov-${TARGET_NAME} : Generates HTML code coverage report for the associated named target. +# ccov-all : Generates HTML code coverage report, merging every target added with 'ALL' parameter into a single detailed report. +# +# LLVM-COV: +# ccov : Generates HTML code coverage report for every target added with 'AUTO' parameter. +# ccov-report : Generates HTML code coverage report for every target added with 'AUTO' parameter. +# ccov-${TARGET_NAME} : Generates HTML code coverage report. +# ccov-report-${TARGET_NAME} : Prints to command line summary per-file coverage information. +# ccov-export-${TARGET_NAME} : Exports the coverage report to a JSON file. +# ccov-show-${TARGET_NAME} : Prints to command line detailed per-line coverage information. +# ccov-all : Generates HTML code coverage report, merging every target added with 'ALL' parameter into a single detailed report. +# ccov-all-report : Prints summary per-file coverage information for every target added with ALL' parameter to the command line. +# ccov-all-export : Exports the coverage report to a JSON file. +# +# Required: +# TARGET_NAME - Name of the target to generate code coverage for. +# Optional: +# PUBLIC - Sets the visibility for added compile options to targets to PUBLIC instead of the default of PRIVATE. +# INTERFACE - Sets the visibility for added compile options to targets to INTERFACE instead of the default of PRIVATE. +# PLAIN - Do not set any target visibility (backward compatibility with old cmake projects) +# AUTO - Adds the target to the 'ccov' target so that it can be run in a batch with others easily. Effective on executable targets. +# ALL - Adds the target to the 'ccov-all' and 'ccov-all-report' targets, which merge several executable targets coverage data to a single report. Effective on executable targets. +# EXTERNAL - For GCC's lcov, allows the profiling of 'external' files from the processing directory +# COVERAGE_TARGET_NAME - For executables ONLY, changes the outgoing target name so instead of `ccov-${TARGET_NAME}` it becomes `ccov-${COVERAGE_TARGET_NAME}`. +# EXCLUDE - Excludes files of the patterns provided from coverage. Note that GCC/lcov excludes by glob pattern, and clang/LLVM excludes via regex! **These do not copy to the 'all' targets.** +# OBJECTS - For executables ONLY, if the provided targets are shared libraries, adds coverage information to the output +# ARGS - For executables ONLY, appends the given arguments to the associated ccov-* executable call +# ~~~ +function(target_code_coverage TARGET_NAME) + # Argument parsing + set(options AUTO ALL EXTERNAL PUBLIC INTERFACE PLAIN) + set(single_value_keywords COVERAGE_TARGET_NAME) + set(multi_value_keywords EXCLUDE OBJECTS ARGS) + cmake_parse_arguments( + target_code_coverage "${options}" "${single_value_keywords}" + "${multi_value_keywords}" ${ARGN}) + + # Set the visibility of target functions to PUBLIC, INTERFACE or default to + # PRIVATE. + if(target_code_coverage_PUBLIC) + set(TARGET_VISIBILITY PUBLIC) + set(TARGET_LINK_VISIBILITY PUBLIC) + elseif(target_code_coverage_INTERFACE) + set(TARGET_VISIBILITY INTERFACE) + set(TARGET_LINK_VISIBILITY INTERFACE) + elseif(target_code_coverage_PLAIN) + set(TARGET_VISIBILITY PUBLIC) + set(TARGET_LINK_VISIBILITY) + else() + set(TARGET_VISIBILITY PRIVATE) + set(TARGET_LINK_VISIBILITY PRIVATE) + endif() + + if(NOT target_code_coverage_COVERAGE_TARGET_NAME) + # If a specific name was given, use that instead. + set(target_code_coverage_COVERAGE_TARGET_NAME ${TARGET_NAME}) + endif() + + if(CODE_COVERAGE) + + # Add code coverage instrumentation to the target's linker command + if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" + OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") + target_compile_options(${TARGET_NAME} ${TARGET_VISIBILITY} + -fprofile-instr-generate -fcoverage-mapping) + target_link_options(${TARGET_NAME} ${TARGET_VISIBILITY} + -fprofile-instr-generate -fcoverage-mapping) + elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES + "GNU") + target_compile_options(${TARGET_NAME} ${TARGET_VISIBILITY} -fprofile-arcs + -ftest-coverage) + target_link_libraries(${TARGET_NAME} ${TARGET_LINK_VISIBILITY} gcov) + endif() + + # Targets + get_target_property(target_type ${TARGET_NAME} TYPE) + + # Add shared library to processing for 'all' targets + if(target_type STREQUAL "SHARED_LIBRARY" AND target_code_coverage_ALL) + if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" + OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") + add_custom_target( + ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND + ${CMAKE_COMMAND} -E echo "-object=$" >> + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list + DEPENDS ccov-preprocessing ${TARGET_NAME}) + + if(NOT TARGET ccov-libs) + message( + FATAL_ERROR + "Calling target_code_coverage with 'ALL' must be after a call to 'add_code_coverage_all_targets'." + ) + endif() + + add_dependencies(ccov-libs + ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}) + endif() + endif() + + # For executables add targets to run and produce output + if(target_type STREQUAL "EXECUTABLE") + if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" + OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") + + # If there are shared objects to also work with, generate the string to + # add them here + foreach(SO_TARGET ${target_code_coverage_OBJECTS}) + # Check to see if the target is a shared object + if(TARGET ${SO_TARGET}) + get_target_property(SO_TARGET_TYPE ${SO_TARGET} TYPE) + if(${SO_TARGET_TYPE} STREQUAL "SHARED_LIBRARY") + set(SO_OBJECTS ${SO_OBJECTS} -object=$) + endif() + endif() + endforeach() + + # Run the executable, generating raw profile data Make the run data + # available for further processing. Separated to allow Windows to run + # this target serially. + add_custom_target( + ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND + ${CMAKE_COMMAND} -E env + LLVM_PROFILE_FILE=${target_code_coverage_COVERAGE_TARGET_NAME}.profraw + $ ${target_code_coverage_ARGS} + COMMAND + ${CMAKE_COMMAND} -E echo "-object=$" + ${SO_OBJECTS} >> ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list + COMMAND + ${CMAKE_COMMAND} -E echo + "${CMAKE_CURRENT_BINARY_DIR}/${target_code_coverage_COVERAGE_TARGET_NAME}.profraw" + >> ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list + JOB_POOL ccov_serial_pool + DEPENDS ccov-preprocessing ccov-libs ${TARGET_NAME}) + + # Merge the generated profile data so llvm-cov can process it + add_custom_target( + ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND + ${LLVM_PROFDATA_PATH} merge -sparse + ${target_code_coverage_COVERAGE_TARGET_NAME}.profraw -o + ${target_code_coverage_COVERAGE_TARGET_NAME}.profdata + DEPENDS ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}) + + # Ignore regex only works on LLVM >= 7 + if(LLVM_COV_VERSION VERSION_GREATER_EQUAL "7.0.0") + foreach(EXCLUDE_ITEM ${target_code_coverage_EXCLUDE}) + set(EXCLUDE_REGEX ${EXCLUDE_REGEX} + -ignore-filename-regex='${EXCLUDE_ITEM}') + endforeach() + endif() + + # Print out details of the coverage information to the command line + add_custom_target( + ccov-show-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND + ${LLVM_COV_PATH} show $ ${SO_OBJECTS} + -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata + -show-line-counts-or-regions ${EXCLUDE_REGEX} + DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}) + + # Print out a summary of the coverage information to the command line + add_custom_target( + ccov-report-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND + ${LLVM_COV_PATH} report $ ${SO_OBJECTS} + -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata + ${EXCLUDE_REGEX} + DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}) + + # Export coverage information so continuous integration tools (e.g. + # Jenkins) can consume it + add_custom_target( + ccov-export-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND + ${LLVM_COV_PATH} export $ ${SO_OBJECTS} + -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata + -format="text" ${EXCLUDE_REGEX} > + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}.json + DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}) + + # Generates HTML output of the coverage information for perusal + add_custom_target( + ccov-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND + ${LLVM_COV_PATH} show $ ${SO_OBJECTS} + -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata + -show-line-counts-or-regions + -output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME} + -format="html" ${EXCLUDE_REGEX} + DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}) + + elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES + "GNU") + set(COVERAGE_INFO + "${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}.info" + ) + + # Run the executable, generating coverage information + add_custom_target( + ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND $ ${target_code_coverage_ARGS} + DEPENDS ccov-preprocessing ${TARGET_NAME}) + + # Generate exclusion string for use + foreach(EXCLUDE_ITEM ${target_code_coverage_EXCLUDE}) + set(EXCLUDE_REGEX ${EXCLUDE_REGEX} --remove ${COVERAGE_INFO} + '${EXCLUDE_ITEM}') + endforeach() + + if(EXCLUDE_REGEX) + set(EXCLUDE_COMMAND ${LCOV_PATH} ${EXCLUDE_REGEX} --output-file + ${COVERAGE_INFO}) + else() + set(EXCLUDE_COMMAND ;) + endif() + + if(NOT ${target_code_coverage_EXTERNAL}) + set(EXTERNAL_OPTION --no-external) + endif() + + # Capture coverage data + if(${CMAKE_VERSION} VERSION_LESS "3.17.0") + add_custom_target( + ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND ${CMAKE_COMMAND} -E remove -f ${COVERAGE_INFO} + COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --zerocounters + COMMAND $ ${target_code_coverage_ARGS} + COMMAND + ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --base-directory + ${CMAKE_SOURCE_DIR} --capture ${EXTERNAL_OPTION} --output-file + ${COVERAGE_INFO} + COMMAND ${EXCLUDE_COMMAND} + DEPENDS ccov-preprocessing ${TARGET_NAME}) + else() + add_custom_target( + ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND ${CMAKE_COMMAND} -E rm -f ${COVERAGE_INFO} + COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --zerocounters + COMMAND $ ${target_code_coverage_ARGS} + COMMAND + ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --base-directory + ${CMAKE_SOURCE_DIR} --capture ${EXTERNAL_OPTION} --output-file + ${COVERAGE_INFO} + COMMAND ${EXCLUDE_COMMAND} + DEPENDS ccov-preprocessing ${TARGET_NAME}) + endif() + + # Generates HTML output of the coverage information for perusal + add_custom_target( + ccov-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND + ${GENHTML_PATH} -o + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME} + ${COVERAGE_INFO} + DEPENDS ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME}) + endif() + + add_custom_command( + TARGET ccov-${target_code_coverage_COVERAGE_TARGET_NAME} + POST_BUILD + COMMAND ; + COMMENT + "Open ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}/index.html in your browser to view the coverage report." + ) + + # AUTO + if(target_code_coverage_AUTO) + if(NOT TARGET ccov) + add_custom_target(ccov) + endif() + add_dependencies(ccov ccov-${target_code_coverage_COVERAGE_TARGET_NAME}) + + if(NOT CMAKE_C_COMPILER_ID MATCHES "GNU" AND NOT CMAKE_CXX_COMPILER_ID + MATCHES "GNU") + if(NOT TARGET ccov-report) + add_custom_target(ccov-report) + endif() + add_dependencies( + ccov-report + ccov-report-${target_code_coverage_COVERAGE_TARGET_NAME}) + endif() + endif() + + # ALL + if(target_code_coverage_ALL) + if(NOT TARGET ccov-all-processing) + message( + FATAL_ERROR + "Calling target_code_coverage with 'ALL' must be after a call to 'add_code_coverage_all_targets'." + ) + endif() + + add_dependencies(ccov-all-processing + ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}) + endif() + endif() + endif() +endfunction() + +# Adds code coverage instrumentation to all targets in the current directory and +# any subdirectories. To add coverage instrumentation to only specific targets, +# use `target_code_coverage`. +function(add_code_coverage) + if(CODE_COVERAGE) + if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" + OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") + add_compile_options(-fprofile-instr-generate -fcoverage-mapping) + add_link_options(-fprofile-instr-generate -fcoverage-mapping) + elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES + "GNU") + add_compile_options(-fprofile-arcs -ftest-coverage) + link_libraries(gcov) + endif() + endif() +endfunction() + +# Adds the 'ccov-all' type targets that calls all targets added via +# `target_code_coverage` with the `ALL` parameter, but merges all the coverage +# data from them into a single large report instead of the numerous smaller +# reports. Also adds the ccov-all-capture Generates an all-merged.info file, for +# use with coverage dashboards (e.g. codecov.io, coveralls). +# ~~~ +# Optional: +# EXCLUDE - Excludes files of the patterns provided from coverage. Note that GCC/lcov excludes by glob pattern, and clang/LLVM excludes via regex! +# ~~~ +function(add_code_coverage_all_targets) + # Argument parsing + set(multi_value_keywords EXCLUDE) + cmake_parse_arguments(add_code_coverage_all_targets "" "" + "${multi_value_keywords}" ${ARGN}) + + if(CODE_COVERAGE) + if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" + OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") + + # Merge the profile data for all of the run executables + if(WIN32) + add_custom_target( + ccov-all-processing + COMMAND + powershell -Command $$FILELIST = Get-Content + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list\; llvm-profdata.exe + merge -o ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata + -sparse $$FILELIST) + else() + add_custom_target( + ccov-all-processing + COMMAND + ${LLVM_PROFDATA_PATH} merge -o + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata -sparse `cat + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list`) + endif() + + # Regex exclude only available for LLVM >= 7 + if(LLVM_COV_VERSION VERSION_GREATER_EQUAL "7.0.0") + foreach(EXCLUDE_ITEM ${add_code_coverage_all_targets_EXCLUDE}) + set(EXCLUDE_REGEX ${EXCLUDE_REGEX} + -ignore-filename-regex='${EXCLUDE_ITEM}') + endforeach() + endif() + + # Print summary of the code coverage information to the command line + if(WIN32) + add_custom_target( + ccov-all-report + COMMAND + powershell -Command $$FILELIST = Get-Content + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list\; llvm-cov.exe + report $$FILELIST + -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata + ${EXCLUDE_REGEX} + DEPENDS ccov-all-processing) + else() + add_custom_target( + ccov-all-report + COMMAND + ${LLVM_COV_PATH} report `cat + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list` + -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata + ${EXCLUDE_REGEX} + DEPENDS ccov-all-processing) + endif() + + # Export coverage information so continuous integration tools (e.g. + # Jenkins) can consume it + add_custom_target( + ccov-all-export + COMMAND + ${LLVM_COV_PATH} export `cat + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list` + -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata + -format="text" ${EXCLUDE_REGEX} > + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/coverage.json + DEPENDS ccov-all-processing) + + # Generate HTML output of all added targets for perusal + if(WIN32) + add_custom_target( + ccov-all + COMMAND + powershell -Command $$FILELIST = Get-Content + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list\; llvm-cov.exe show + $$FILELIST + -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata + -show-line-counts-or-regions + -output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged + -format="html" ${EXCLUDE_REGEX} + DEPENDS ccov-all-processing) + else() + add_custom_target( + ccov-all + COMMAND + ${LLVM_COV_PATH} show `cat + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list` + -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata + -show-line-counts-or-regions + -output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged + -format="html" ${EXCLUDE_REGEX} + DEPENDS ccov-all-processing) + endif() + + elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES + "GNU") + set(COVERAGE_INFO "${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.info") + + # Nothing required for gcov + add_custom_target(ccov-all-processing COMMAND ;) + + # Exclusion regex string creation + set(EXCLUDE_REGEX) + foreach(EXCLUDE_ITEM ${add_code_coverage_all_targets_EXCLUDE}) + set(EXCLUDE_REGEX ${EXCLUDE_REGEX} --remove ${COVERAGE_INFO} + '${EXCLUDE_ITEM}') + endforeach() + + if(EXCLUDE_REGEX) + set(EXCLUDE_COMMAND ${LCOV_PATH} ${EXCLUDE_REGEX} --output-file + ${COVERAGE_INFO}) + else() + set(EXCLUDE_COMMAND ;) + endif() + + # Capture coverage data + if(${CMAKE_VERSION} VERSION_LESS "3.17.0") + add_custom_target( + ccov-all-capture + COMMAND ${CMAKE_COMMAND} -E remove -f ${COVERAGE_INFO} + COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --capture + --output-file ${COVERAGE_INFO} + COMMAND ${EXCLUDE_COMMAND} + DEPENDS ccov-preprocessing ccov-all-processing) + else() + add_custom_target( + ccov-all-capture + COMMAND ${CMAKE_COMMAND} -E rm -f ${COVERAGE_INFO} + COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --capture + --output-file ${COVERAGE_INFO} + COMMAND ${EXCLUDE_COMMAND} + DEPENDS ccov-preprocessing ccov-all-processing) + endif() + + # Generates HTML output of all targets for perusal + add_custom_target( + ccov-all + COMMAND ${GENHTML_PATH} -o ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged + ${COVERAGE_INFO} -p ${CMAKE_SOURCE_DIR} + DEPENDS ccov-all-capture) + + endif() + + add_custom_command( + TARGET ccov-all + POST_BUILD + COMMAND ; + COMMENT + "Open ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged/index.html in your browser to view the coverage report." + ) + endif() +endfunction() diff --git a/cmake/cxx.cmake b/cmake/cxx.cmake new file mode 100644 index 00000000..fa6efb89 --- /dev/null +++ b/cmake/cxx.cmake @@ -0,0 +1,98 @@ +# ---------------------------------------------------------------- +# use this in subdirs that compile c++ code +# +macro(xo_include_options target) + # ---------------------------------------------------------------- + # PROJECT_SOURCE_DIR: + # so we can for example write + # #include "ordinaltree/foo.hpp" + # from anywhere in the project + # PROJECT_BINARY_DIR: + # since generated version file will be in build directory, + # need that build directory to also appear in + # compiler's include path + # + target_include_directories( + ${target} PUBLIC + $ # e.g. for #include "indentlog/scope.hpp" + $ + $ # e.g. for #include "Refcounted.hpp" in refcnt/src + $ + $ # e.g. for generated config.hpp file + ) + + # ---------------------------------------------------------------- + # make standard directories for std:: includes explicit + # so that + # (1) they appear in compile_commands.json. + # (2) clangd (run from emacs lsp-mode) can find them + # + if(CMAKE_EXPORT_COMPILE_COMMANDS) + set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) + endif() +endmacro() + +# ---------------------------------------------------------------- +# variable +# XO_ADDRESS_SANITIZE +# determines whether to enable address sanitizer for the XO project +# (see toplevel CMakeLists.txt) +# ---------------------------------------------------------------- +if(XO_ADDRESS_SANITIZE) + add_compile_options(-fsanitize=address) + add_link_options(-fsanitize=address) +endif() + +# XO_STANDARD_COMPILE_OPTIONS: use these when XO_ADDRESS_SANITIZE=OFF +set(XO_STANDARD_COMPILE_OPTIONS -Werror -Wall -Wextra) + +# XO_ADDRESS_SANITIZE_COMPILE_OPTIONS: use when XO_ADDRESS_SANITIZE=ON +# +# address sanitizer build complains about _FORTIFY_SOURCE redefines +# In file included from :460: +# :1:9: error: '_FORTIFY_SOURCE' macro redefined [-Werror,-Wmacro-redefined] +# #define _FORTIFY_SOURCE 2 +# +set(XO_ADDRESS_SANITIZE_COMPILE_OPTIONS -Werror -Wall -Wextra -Wno-macro-redefined) + +# XO_COMPILE_OPTIONS: use these with xo_compile_options() macro +if(XO_ADDRESS_SANITIZE) + set(XO_COMPILE_OPTIONS ${XO_ADDRESS_SANITIZE_COMPILE_OPTIONS}) +else() + set(XO_COMPILE_OPTIONS ${XO_STANDARD_COMPILE_OPTIONS}) +endif() + +# ---------------------------------------------------------------- +# generally want all the errors+warnings! +# however: address sanitizer generates error on _FORTIFY_SOURCE +# +macro(xo_compile_options target) + target_compile_options(${target} PRIVATE ${XO_COMPILE_OPTIONS}) +endmacro() + +# ---------------------------------------------------------------- +# use this for a subdir that builds a library +# EXPORT drives .cmake config files intended for consumption +# by higher-level cmake projects via find_package() +# +macro(xo_install_library target) + install( + TARGETS ${target} + EXPORT ${target}Targets + LIBRARY DESTINATION lib COMPONENT Runtime + ARCHIVE DESTINATION lib COMPONENT Development + RUNTIME DESTINATION bin COMPONENT Runtime + PUBLIC_HEADER DESTINATION include COMPONENT Development + BUNDLE DESTINATION bin COMPONENT Runtime + ) +endmacro() + +# ---------------------------------------------------------------- +# use this when relying on indentlog [[https://github.com/rconybea/indentlog]] headers +# +macro(xo_indentlog_dependency target) + find_package(indentlog REQUIRED) + #add_dependencies(${target} indentlog) + target_link_libraries(${target} PUBLIC indentlog) + #target_include_directories(${target} PUBLIC ${indentlog_DIR}/../../../include) +endmacro() diff --git a/cmake/reflectConfig.cmake.in b/cmake/reflectConfig.cmake.in new file mode 100644 index 00000000..e13a2c54 --- /dev/null +++ b/cmake/reflectConfig.cmake.in @@ -0,0 +1,4 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/@XO_PROJECT_NAME@Targets.cmake") +check_required_components("@PROJECT_NAME@") diff --git a/cmake/run-external-ctest b/cmake/run-external-ctest new file mode 100755 index 00000000..386c45a4 --- /dev/null +++ b/cmake/run-external-ctest @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +# $1 = build directory + +cd $1 +shift + +ctest "${@}" diff --git a/include/reflect/CMakeLists.txt b/include/reflect/CMakeLists.txt new file mode 100644 index 00000000..a7e07dd6 --- /dev/null +++ b/include/reflect/CMakeLists.txt @@ -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 diff --git a/include/reflect/EstablishTypeDescr.hpp b/include/reflect/EstablishTypeDescr.hpp new file mode 100644 index 00000000..2b0e25a7 --- /dev/null +++ b/include/reflect/EstablishTypeDescr.hpp @@ -0,0 +1,62 @@ +/* file EstablishTypeDescr.hpp + * + * author: Roland Conybeare, Aug 2022 + */ + +#pragma once + +#include "reflect/TypeDescr.hpp" +#include "reflect/TaggedPtr.hpp" + +namespace xo { + namespace reflect { + class EstablishTypeDescr { + public: + /* implementation method; expect this to be used only within reflect/ library. + * avoids some otherwise-cyclic #include paths + * between specialized headers such as vector/VectorTdx.hpp and this + * EstablishTypeDescr.hpp + */ +#ifdef OBSOLETE + template + static TaggedPtr establish_tp(T * x) { return TaggedPtr(establish(), x); } +#endif + template + static TaggedPtr establish_most_derived_tp(T * x) { return establish()->most_derived_self_tp(x); } + + template + static TypeDescrW establish() { + TypeDescrW td = TypeDescrBase::require(&typeid(T), + type_name(), + nullptr); + +#ifdef NOT_USING + std::function to_self_tp; + + if (std::is_base_of_v) { + /* T is a descendant of SelfTagging (or T = SelfTagging); + * use SelfTagging.self_tp() + */ + to_self_tp = [](void * x) { return reinterpret_cast(x)->self_tp(); }; + } else { + /* T is not a descendant of SelfTagging. + * want to return + */ + to_self_tp = [td](void * x) { return TaggedPtr(td, x); }; + } + + td->assign_to_self_tp(to_self_tp); +#endif + return td; + } + }; /*EstablishTypeDescr*/ + + template + inline TaggedPtr establish_most_derived_tp(T * x) { + return EstablishTypeDescr::establish_most_derived_tp(x); + } + } /*namespace reflect*/ +} /*namespace xo*/ + + +/* end EstablishTypeDescr.hpp */ diff --git a/include/reflect/Metatype.hpp b/include/reflect/Metatype.hpp new file mode 100644 index 00000000..23f3fd10 --- /dev/null +++ b/include/reflect/Metatype.hpp @@ -0,0 +1,38 @@ +/* @file Metatype.hpp */ + +#pragma once + +#include + +namespace xo { + namespace reflect { + enum class Metatype { mt_invalid, mt_atomic, mt_pointer, mt_vector, mt_struct }; + + inline std::ostream & operator<<(std::ostream & os, + Metatype x) { + switch(x) { + case Metatype::mt_invalid: + os << "invalid!"; + break; + case Metatype::mt_atomic: + os << "atomic"; + break; + case Metatype::mt_pointer: + os << "pointer"; + break; + case Metatype::mt_vector: + os << "vector"; + break; + case Metatype::mt_struct: + os << "struct"; + break; + default: + os << "???"; + } + return os; + } /*operator<<*/ + + } /*namespace reflect*/ +} /*namespace xo*/ + +/* end Metatype.hpp */ diff --git a/include/reflect/Reflect.hpp b/include/reflect/Reflect.hpp new file mode 100644 index 00000000..73ef68f3 --- /dev/null +++ b/include/reflect/Reflect.hpp @@ -0,0 +1,235 @@ +/* file Reflect.hpp + * + * author: Roland Conybeare, Aug 2022 + */ + +#pragma once + +#include "reflect/SelfTagging.hpp" +#include "reflect/EstablishTypeDescr.hpp" +#include "reflect/atomic/AtomicTdx.hpp" +#include "reflect/pointer/PointerTdx.hpp" +#include "reflect/vector/VectorTdx.hpp" +#include "reflect/struct/StructTdx.hpp" +#include "refcnt/Refcounted.hpp" +#include +#include +#include // for std::pair<> + +namespace xo { + namespace reflect { + template + class EstablishTdx { + public: + static std::unique_ptr make() { return AtomicTdx::make(); } + }; /*EstablishTdx*/ + + // ----- xo::ref::rp ----- + + /* definition provide after decl for Reflect {} below */ + template + class EstablishTdx> { + public: + static std::unique_ptr make(); + }; /*EstablishTdx*/ + + // ----- std::array ----- + + /* definition provide after decl for Reflect {} below */ + template + class EstablishTdx> { + public: + static std::unique_ptr make(); + }; /*EstablishTdx*/ + + // ----- std::vector ----- + + /* definition provide after decl for Reflect {} below */ + template + class EstablishTdx> { + public: + static std::unique_ptr make(); + }; /*EstablishTdx*/ + + // ----- std::pair ----- + + /* definition provide after decl for Reflect {} below */ + template + class EstablishTdx> { + public: + static std::unique_ptr make(); + }; /*EstablishTdx*/ + + // ----- MakeTagged ----- + + template + class TaggedPtrMaker { + public: + static TaggedPtr make_tp(T * x); + static TaggedRcptr make_rctp(T * x); + }; + + template<> + class TaggedPtrMaker { + public: + static TaggedPtr make_tp(SelfTagging * x) { + return x->self_tp(); + } /*make_tp*/ + + static TaggedRcptr make_rctp(SelfTagging * x) { + return x->self_tp(); + } /*make_rctp*/ + }; /*TaggedPtrMaker*/ + + // ----- Reflect ----- + + class Reflect { + public: + /* Use: + * using mytype = ...; + * if (Reflect::is_reflected()) { ... } + */ + template + static bool is_reflected() { return TypeDescrBase::is_reflected(&typeid(T)); } + + /* Use: + * using mytype = ...; + * TypeDescrW td = Reflect::require(); + * + * Note: + * To avoid cyclic header dependencies + * (between EstablishTypeDescr.hpp <-> {vector/VectorTdx.hpp etc.}, + * we use a 2-stage setup process: + * + * 1. EstablishTypeDescr::establish() creates a TypeDescr* object + * with lowest-common-denominator .tdextra AtomicTdx. + * (see [reflect/EstablishTypeDescr.hpp]) + * + * 2. Reflect::require() upgrades .tdextra to suitable implementation + * depending on T; this means also need to visit reflection info + * (TypeDescr objects) for nested types to upgrade them too. + * + * This allows template-fu for a compound type (like std::vector), + * implemented in specialized header (like [reflect/struct/VectorTdx.hpp]) to + * refer to reflection info for T without having to pull in all the + * headers needed to properly reflect T (like this [reflect/Reflect.hpp]) + * + */ + template + static TypeDescrW require() { + TypeDescrW retval_td = EstablishTypeDescr::establish(); + + /* mark TypeDescr for T as complete (even though it isn't quite yet), + * so that when we encounter recursive types, reflection terminates. + * For example consider type resulting from code like + * + * typename T; + * using T = std::vector; + * + */ + if (retval_td->mark_complete()) { + /* control here on 2nd+later calls to require(). + * in principle can immediately short-circuit. + */ + } else { + /* control comes here the first time require() runs */ + + auto final_tdx = EstablishTdx::make(); + + retval_td->assign_tdextra(std::move(final_tdx)); + + /* also need to require for each child */ + } + + return retval_td; + } /*require*/ + + /* Use: + * T * xyz = ...; + * TaggedPtr xyz_tp = Reflect::make_tp(xyz); + */ + template + static TaggedPtr make_tp(T * x) { return TaggedPtrMaker::make_tp(x); } + + template + static TaggedRcptr make_rctp(T * x) { return TaggedPtrMaker::make_rctp(x); } + }; /*Reflect*/ + + // ----- MakeTagged ----- + + template + TaggedPtr + TaggedPtrMaker::make_tp(T * x) { + return TaggedPtr(Reflect::require(), x); + } /*make_tp*/ + + template + TaggedRcptr + TaggedPtrMaker::make_rctp(T * x) { + return TaggedRcptr(Reflect::require(), x); + } /*make_rctp*/ + + // ----- xo::ref::rp ----- + + /* declared above before + * class Reflect { .. } + */ + template + std::unique_ptr + EstablishTdx>::make() { + /* need to ensure Object is property reflected. + * + * In practice must be a class type, since has to store refcount + * + supply assoc'd incr/decr methods + */ + Reflect::require(); + + return RefPointerTdx>::make(); + } /*make*/ + + // ----- std::array ----- + + /* declared above before + * class Reflect { .. } + */ + template + std::unique_ptr + EstablishTdx>::make() { + /* need to ensure Element is properly reflected */ + Reflect::require(); + + return StdArrayTdx::make(); + } /*make*/ + + // ----- std::vector ----- + + /* declared above before + * class Reflect { .. } + */ + template + std::unique_ptr + EstablishTdx>::make() { + /* need to ensure Element is properly reflected */ + Reflect::require(); + + return StdVectorTdx::make(); + } /*make*/ + + // ----- std::pair ----- + + /* declared above before + * class Reflect { .. } + */ + template + std::unique_ptr + EstablishTdx>::make() { + /* need to ensure Lhs, Rhs are properly reflected */ + Reflect::require(); + Reflect::require(); + + return StructTdx::pair(); + } /*make*/ + } /*namespace reflect*/ +} /*namespace xo*/ + +/* end Reflect.hpp */ diff --git a/include/reflect/SelfTagging.hpp b/include/reflect/SelfTagging.hpp new file mode 100644 index 00000000..29517e33 --- /dev/null +++ b/include/reflect/SelfTagging.hpp @@ -0,0 +1,31 @@ +/* file SelfTagging.hpp + * + * author: Roland Conybeare, Aug 2022 + */ + +#pragma once + +#include "refcnt/Refcounted.hpp" +#include "reflect/TypeDescr.hpp" +#include "reflect/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 */ diff --git a/include/reflect/StructReflector.hpp b/include/reflect/StructReflector.hpp new file mode 100644 index 00000000..f2237578 --- /dev/null +++ b/include/reflect/StructReflector.hpp @@ -0,0 +1,161 @@ +/* @file StructReflector.hpp */ + +#pragma once + +#include "reflect/Reflect.hpp" +#include "reflect/TypeDescr.hpp" +#include "reflect/struct/StructMember.hpp" +#include "reflect/struct/StructTdx.hpp" +#include + +namespace xo { + namespace reflect { + template + class SelfTagger {}; + + template + struct SelfTagger { + static TaggedPtr self_tp(void * object) { + return (reinterpret_cast(object))->self_tp(); + } + }; + + template + struct SelfTagger { + 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 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 + class StructReflector { + public: + using struct_t = StructT; + + public: + StructReflector() : td_{EstablishTypeDescr::establish()} {} + ~StructReflector() { + this->require_complete(); + } + + bool is_complete() const { return s_reflected_flag; } + bool is_incomplete() const { return !s_reflected_flag; } + + template + void reflect_member(std::string const & member_name, + MemberT OwnerT::* member_addr) { + + auto accessor + (GeneralStructMemberAccessor::make(member_addr)); + + /* used to do this in GeneralStructMemberAccessor<> ctor, + * but that introduces #include cycle + */ + Reflect::require(); + + 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; + + /* if self-tagging, can use .self_tp() to get most-derived tagged pointer */ + auto to_self_tp_fn + = ([](void * object) + { + return SelfTagger::self_tp(object); + }); + + auto tdx = StructTdx::make(std::move(this->member_v_), + have_to_self_tp, + to_self_tp_fn); + + this->td_->assign_tdextra(std::move(tdx)); + } + } /*complete*/ + + template + void adopt_ancestors() { + assert(Reflect::is_reflected()); + + TypeDescr ancestor_td = Reflect::require(); + + /* 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()); + } + } /*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 member_v_; + }; /*StructReflector*/ + + template + bool StructReflector::s_reflected_flag = false; + } /*namespace reflect*/ + + /* e.g. + * struct Foo { int bar_; }; + * struct Bar : public Foo { .. }; + * + * StructReflector 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 sr; + * REFLECT_LITERAL_MEMBER(sr, bar_); + * + * then REFLECT_LITERAL_MEMBER() expands to something like: + * sr.reflect_member("bar_", &StructReflector::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 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*/ diff --git a/include/reflect/TaggedPtr.hpp b/include/reflect/TaggedPtr.hpp new file mode 100644 index 00000000..653c8ad7 --- /dev/null +++ b/include/reflect/TaggedPtr.hpp @@ -0,0 +1,125 @@ +/* @file TaggedPtr.hpp */ + +#pragma once + +#include "reflect/TypeDescr.hpp" +//#include "reflect/EstablishTypeDescr.hpp" +#include + +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 + static TaggedPtr make(T * x) { return TaggedPtr(Reflect::require(), 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 + 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 + static void visit_graph(TaggedPtr tp, Fn && visit_fn) { + std::unordered_set visited_set; + + visit_graph_aux(tp, visit_fn, &visited_set); + } /*visit_graph*/ + + TypeDescr td() const { return td_; } + void * address() const { return address_; } + + void assign_td(TypeDescr x) { td_ = x; } + void assign_address(void * x) { address_ = x; } + + bool is_universal_null() const { return (td_ == nullptr) && (address_ == nullptr); } + bool is_vector() const { return td_ && td_->is_vector(); } + bool is_struct() const { return td_ && td_->is_struct(); } + + + /* returns pointer-to-T, if in fact this tagged pointer is understood + * to refer to a T-instance; otherwise nullptr + */ + template + T * recover_native() const { return this->td_->recover_native(this->address_); } + + uint32_t n_child() const { + return this->td_->n_child(this->address_); + } /*n_child*/ + + TaggedPtr get_child(uint32_t i) const { + return this->td_->child_tp(i, this->address_); + } /*get_child*/ + + /* require: + * - .is_struct() is true + */ + std::string const & struct_member_name(uint32_t i) const { + return this->td_->struct_member_name(i); + } + + private: + template + static void visit_graph_aux(TaggedPtr tp, + Fn && visit_fn, + std::unordered_set * 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 */ + + diff --git a/include/reflect/TaggedRcptr.hpp b/include/reflect/TaggedRcptr.hpp new file mode 100644 index 00000000..9ca8b15f --- /dev/null +++ b/include/reflect/TaggedRcptr.hpp @@ -0,0 +1,88 @@ +/* file TaggedRcptr.hpp + * + * author: Roland Conybeare, Aug 2022 + */ + +#pragma once + +#include "reflect/TaggedPtr.hpp" +// causes #include cycle, reflect/Reflect.hpp includes this header +//#include "reflect/Reflect.hpp" +#include "refcnt/Refcounted.hpp" + +namespace xo { + namespace reflect { + /* Tagged reference-counted pointer. + * Like TaggedPtr, but also maintains reference count. + * + * note that refcounting behavior is lost if assigned to a TaggedPtr variable! + */ + class TaggedRcptr : public TaggedPtr { + public: + using Refcount = ref::Refcount; + + public: + TaggedRcptr(TypeDescr td, Refcount * x) : TaggedPtr(td, x) { + ref::intrusive_ptr_add_ref(x); + } + TaggedRcptr(TaggedRcptr const & x) : TaggedPtr(x) { + ref::intrusive_ptr_add_ref(x.rc_address()); + } + TaggedRcptr(TaggedRcptr && x) : TaggedPtr(std::move(x)) { + /* since we're moving from x, need to make sure x.dtor + * doesn't decrement refcount + */ + x.assign_address(nullptr); + } + ~TaggedRcptr() { + ref::intrusive_ptr_release(this->rc_address()); + } + + /* causes #include cycle, see [reflect/Reflect.hpp] */ +#ifdef NOT_IN_USE + /* require: T --isa--> ref::Refcount */ + template + static TaggedRcptr make(T * x) { return TaggedRcptr(Reflect::require(), x); } +#endif + + Refcount * rc_address() const { + return reinterpret_cast(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 */ diff --git a/include/reflect/TypeDescr.hpp b/include/reflect/TypeDescr.hpp new file mode 100644 index 00000000..08614071 --- /dev/null +++ b/include/reflect/TypeDescr.hpp @@ -0,0 +1,302 @@ +/* @file TypeDescr.hpp */ + +#pragma once + +//#include "reflect/atomic/AtomicTdx.hpp" +#include "reflect/TypeDescrExtra.hpp" +#include "cxxutil/demangle.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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(); + */ + template + TypeInfoRef make() { return TypeInfoRef(&typeid(T)); } + + std::size_t hash_code() const { return this->tinfo_->hash_code(); } + char const * impl_name() const { return this->tinfo_->name(); } + + static bool is_equal(TypeInfoRef x, TypeInfoRef y) noexcept { + if (x.hash_code() != y.hash_code()) + return false; + + return ::strcmp(x.impl_name(), y.impl_name()) == 0; + } /*is_equal*/ + + private: + /* native type_info object for encapsulated type */ + std::type_info const * tinfo_ = nullptr; + }; /*TypeInfoRef*/ + } /*namespace reflect*/ +} /*namespace xo*/ + +namespace std { + template <> struct hash { + std::size_t operator()(xo::reflect::TypeInfoRef x) const noexcept { return x.hash_code(); } + }; +} /*namespace std*/ + +namespace xo { + namespace reflect { + inline bool operator==(TypeInfoRef x, TypeInfoRef y) { return TypeInfoRef::is_equal(x, y); } + inline bool operator!=(TypeInfoRef x, TypeInfoRef y) { return !TypeInfoRef::is_equal(x, y); } + +#ifdef NOT_IN_USE + namespace detail { + class HashTypeInfoRef { + public: + std::size_t operator()(TypeInfoRef x) const noexcept { return x.hash_code(); } + }; /*HashTypeInfoRef*/ + + class EqualTypeInfoRef { + public: + bool operator()(TypeInfoRef x, TypeInfoRef y) const noexcept { return TypeInfoRef::is_equal(x, y); } + }; /*EqualTypeInfoRef*/ + } /*namespace detail*/ +#endif + + class TypeDescrExtra; + + /* run-time description for a native c++ type */ + class TypeDescrBase { + public: + /* type-description objects for a type T is unique, + * --> can always use its address + */ + TypeDescrBase(TypeDescrBase const & x) = delete; + + /* test whether a type has been reflected. + * introducing this for unit testing + */ + static bool is_reflected(std::type_info const * tinfo) { + return (s_type_table_map.find(TypeInfoRef(tinfo)) + != s_type_table_map.end()); + } /*is_reflected*/ + + /* NOTE: + * implementation here will be defeated if std::type_info + * objects violate ODR. This occurs with clang + 2-level namespaces, + * so important to linke with --flat_namespace defined. + * See FAQ + * [Build Issues|Q2 - dynamic_cast> fails] + */ + static TypeDescrW require(std::type_info const * tinfo, + std::string_view canonical_name, + std::unique_ptr tdextra); + + /* print table of reflected types to os */ + static void print_reflected_types(std::ostream & os); + + TypeId id() const { return id_; } + std::type_info const * typeinfo() const { return typeinfo_; } + std::string_view const & canonical_name() const { return canonical_name_; } + std::string_view const & short_name() const { return short_name_; } + bool complete_flag() const { return complete_flag_; } + TypeDescrExtra * tdextra() const { return tdextra_.get(); } + Metatype metatype() const { return tdextra_->metatype(); } + + /* true iff the type represented by *this is the same as the type T. + * + * Warning: comparing typeinfo address can give false negatives. + * suspect this is caused by problems coalescing linker symbols + * in the clang toolchain. + */ + template + bool is_native() const { + return ((this->typeinfo() == &typeid(T)) + || (this->typeinfo()->hash_code() == typeid(T).hash_code()) + || (this->typeinfo()->name() == typeid(T).name())); + } /*is_native*/ + + /* safe downcast -- like dynamic_cast<>, but does not require a source type */ + template + T * recover_native(void * address) const { + if (this->is_native()) { + return reinterpret_cast(address); + } else { + return nullptr; + } + } /*recover_native*/ + + bool is_vector() const { return this->tdextra_->is_vector(); } + bool is_struct() const { return this->tdextra_->is_struct(); } + + /* given a T-instance object, return tagged pointer with T replaced + * by the most-derived-subtype of T to which *object belongs. + * This works only for descendants of reflect::SelfTagging + */ + TaggedPtr most_derived_self_tp(void * object) const; + + /* if generalized vector (std::vector, std::array, ..): + * .n_child() reports #of elements + * if struct/class: + * .n_child() reports #of instance variables (that have been reflected) + */ + uint32_t n_child(void * object) const { return this->tdextra_->n_child(object); } + TaggedPtr child_tp(uint32_t i, void * object) const; + + /* require: + * - .is_struct() = true + * - i in [0 .. .n_child() - 1] + */ + std::string const & struct_member_name(uint32_t i) const { + return this->tdextra_->struct_member_name(i); + } + /* fetch runtime description for i'th reflected instance variable. + * + * require: + * - .is_struct() = true + * - i in [0 .. .n_child() - 1] + */ + StructMember const & struct_member(uint32_t i) const { + StructMember const * sm = this->tdextra_->struct_member(i); + + assert(sm); + return *sm; + } /*struct_member*/ + + void display(std::ostream & os) const; + std::string display_string() const; + + /* mark this TypeDescr complete; + * returns the value of .complete_flag from _before_ + * this call + */ + bool mark_complete(); + + /* call this once to attach extended type information to a type-description + * (e.g. description of struct members for a record type) + */ + void assign_tdextra(std::unique_ptr tdx); + + private: + TypeDescrBase(TypeId id, + std::type_info const * tinfo, + std::string_view canonical_name, + std::unique_ptr tdextra); + + private: + /* invariant: + * - for all TypeDescrImpl instances x: + * - s_type_table_v[x->id()] = x + * - s_type_table_map[TypeInfoRef(x->typeinfo())] = x + */ + + /* hashmap of all TypeDescr instances, indexed by . singleton */ + static std::unordered_map> s_type_table_map; + /* hashmap of (presumed) duplicate TypeInfoRef values. + * This happens with clang sometimes when the same type is referenced + * from multiple modules (i.e. shared libs). + */ + static std::unordered_map s_coalesced_type_table_map; + + /* vector of all TypeDescr instances. singleton. */ + static std::vector s_type_table_v; + + private: + /* unique id# for this type */ + TypeId id_; + /* typeinfo for type T */ + std::type_info const * typeinfo_ = nullptr; + /* canonical name for this type (see demangle.hpp for type_name()) + * e.g. + * xo::option::Px2 + */ + std::string_view canonical_name_; + /* suffix of .canonical_name, just after last ':' + * e.g. + * Px2 + */ + std::string_view short_name_; + /* set to true once final value for .tdextra is established + * intially all TypeDescr objects will use AtomicTdx for .tdextra + * Reflect::require() upgrades .tdextra for particular types. + * When that procedure makes a decision for a type T, + * .complete_flag will be set to true for the corresponding TypeDescrBase instance + */ + bool complete_flag_ = false; + /* additional type information that either: + * (a) isn't universal across all types, + * e.g. dereferencing instance of a pointer type + * (b) can't be captured with template-fu, + * e.g. struct member names + * + * generally .tdextra will be populated some time after TypeDescrBase's ctor exits. + * This is necessary because of (b) above, also because of possibility of recursive + * types. + */ + std::unique_ptr tdextra_; + }; /*TypeDescrBase*/ + + inline std::ostream & + operator<<(std::ostream & os, TypeDescrBase const & x) { + x.display(os); + return os; + } /*operator<<*/ + + } /*namespace reflect*/ +} /*namespace xo*/ + +/* end TypeDescr.hpp */ diff --git a/include/reflect/TypeDescrExtra.hpp b/include/reflect/TypeDescrExtra.hpp new file mode 100644 index 00000000..2d2be7fa --- /dev/null +++ b/include/reflect/TypeDescrExtra.hpp @@ -0,0 +1,65 @@ +/* @file TypeDescrExtra.hpp */ + +#pragma once + +#include "reflect/Metatype.hpp" +#include +/* note: this file #include'd into TypeDescr.hpp */ +#include + +namespace xo { + namespace reflect { + /* forward-declaring here. see [reflect/struct/StructMember.hpp] */ + class StructMember; + class TypeDescrBase; + class TaggedPtr; + + /* information associated with a c++ type. + * distinct from TypeDescrImpl: + * 1. want to use reflection to support for runtime polymorphism over similar but + * not directly-related types: for example + * std::vector + * and + * std::list + * are both ordered collections + * 2. some information can't be universally established via template-fu, + * for example struct member names + * 3. descriptions for recursive types require 2-stage construction + * + * A TypeDescrImpl instance will contain a pointer to a suitable + * TypeDescrExtra instance. + * + * The single TypeDescrImpl instance for some type T can be established + * automatically, see Reflect::require(). + * + * A specific TypeDescrExtra instance may be attached in a non-automated way + * later + */ + class TypeDescrExtra { + public: + using uint32_t = std::uint32_t; + + public: + virtual ~TypeDescrExtra() = default; + + bool is_vector() const { return this->metatype() == Metatype::mt_vector; } + bool is_struct() const { return this->metatype() == Metatype::mt_struct; } + + virtual Metatype metatype() const = 0; + /* given a T-instance, report most-derived subtype of T to which *object belongs. + * this works only for types that are derived from reflect::SelfTagging. + */ + virtual TaggedPtr most_derived_self_tp(TypeDescrBase const * object_td, void * object) const; + virtual uint32_t n_child(void * object) const = 0; + virtual TaggedPtr child_tp(uint32_t i, void * object) const = 0; + /* require: + * .is_struct() + */ + virtual std::string const & struct_member_name(uint32_t i) const = 0; + /* nullptr unless *this represents a struct/class type */ + virtual StructMember const * struct_member(uint32_t i) const; + }; /*TypeDescrExtra*/ + } /*namespace reflect*/ +} /*namespace xo*/ + +/* end TypeDescrExtra.hpp */ diff --git a/include/reflect/TypeDrivenMap.hpp b/include/reflect/TypeDrivenMap.hpp new file mode 100644 index 00000000..c38c8739 --- /dev/null +++ b/include/reflect/TypeDrivenMap.hpp @@ -0,0 +1,47 @@ +/* @file TypeDrivenMap.hpp + * + * author: Roland Conybeare, Aug 2022 + */ + +#pragma once + +#include "reflect/TypeDescr.hpp" +#include + +namespace xo { + namespace reflect { + /* represents a map :: TypeId -> Value */ + template + 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 contents_v_; + }; /*TypeDrivenMap*/ + } /*namespace reflect*/ +} /*namespace xo*/ + + +/* end TypeDrivenMap.hpp */ diff --git a/include/reflect/atomic/AtomicTdx.hpp b/include/reflect/atomic/AtomicTdx.hpp new file mode 100644 index 00000000..7b2e042d --- /dev/null +++ b/include/reflect/atomic/AtomicTdx.hpp @@ -0,0 +1,37 @@ +/* @file AtomicTdx.hpp */ + +#pragma once + +#include "reflect/TypeDescrExtra.hpp" +//#include "reflect/TaggedPtr.hpp" +#include + +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 make(); + + // ----- Inherited from TypeDescrExtra ----- + + virtual Metatype metatype() const override { return Metatype::mt_atomic; } + virtual uint32_t n_child(void * /*object*/) const override { return 0; } + virtual TaggedPtr child_tp(uint32_t /*i*/, void * /*object*/) const override; + virtual std::string const & struct_member_name(uint32_t i) const override; + //virtual StructMember const * struct_member(uint32_t /*i*/) const override { return nullptr; } + + private: + AtomicTdx() = default; + }; /*TypeDescrExtra*/ + } /*namespace reflect*/ +} /*namespace xo*/ + +/* end AtomicTdx.hpp */ diff --git a/include/reflect/init_reflect.hpp b/include/reflect/init_reflect.hpp new file mode 100644 index 00000000..00506a25 --- /dev/null +++ b/include/reflect/init_reflect.hpp @@ -0,0 +1,22 @@ +/* file init_reflect.hpp + * + * author: Roland Conybeare, Sep 2022 + */ + +#pragma once + +#include "subsys/Subsystem.hpp" + +namespace xo { + /* tag to represent the reflect/ subsystem within ordered initialization */ + enum S_reflect_tag {}; + + template<> + struct InitSubsys { + static void init(); + static InitEvidence require(); + }; +} /*namespace xo*/ + + +/* end init_reflect.hpp */ diff --git a/include/reflect/pointer/PointerTdx.hpp b/include/reflect/pointer/PointerTdx.hpp new file mode 100644 index 00000000..d2d3b868 --- /dev/null +++ b/include/reflect/pointer/PointerTdx.hpp @@ -0,0 +1,76 @@ +/* file PointerTdx.hpp + * + * author: Roland Conybeare, Sep 2022 + */ + +#pragma once + +#include "reflect/TypeDescrExtra.hpp" +#include "reflect/EstablishTypeDescr.hpp" +#include "indentlog/scope.hpp" + +namespace xo { + namespace reflect { + /* Extra type-associated information for a pointer-like type + * + * Treat a pointer as a container that has 0 or 1 children; + * - 0 children if null + * - 1 child otherwise + */ + class PointerTdx : public TypeDescrExtra { + public: + // ----- Inherited from TypeDescrExtra ----- + + virtual Metatype metatype() const override { return Metatype::mt_pointer; } + virtual uint32_t n_child(void * object) const override = 0; + virtual TaggedPtr child_tp(uint32_t i, void * object) const override = 0; + /* (forbidden) */ + virtual std::string const & struct_member_name(uint32_t i) const override; + }; /*PointerTdx*/ + + // ----- RefPointerTdx ----- + + /* xo::ref::intrusive_ptr for some T */ + template + class RefPointerTdx : public PointerTdx { + public: + using target_t = Pointer; + + static std::unique_ptr make() { + return std::unique_ptr(new RefPointerTdx()); + } /*make*/ + + virtual uint32_t n_child(void * object) const override { + /* e.g: + * target_t = ref::rp + */ + target_t * ptr = reinterpret_cast(object); + + if (*ptr) + return 1; + else + return 0; + } /*n_child*/ + + virtual TaggedPtr child_tp(uint32_t i, void * object) const override { + using xo::tostr; + using xo::xtag; + + target_t * ptr = reinterpret_cast(object); + + if (i > 0) { + throw std::runtime_error(tostr("RefPointerTdx::child_tp" + ": attempt to fetch child #i from a ref::rp", + xtag("T", type_name()), + 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 */ diff --git a/include/reflect/struct/StructMember.hpp b/include/reflect/struct/StructMember.hpp new file mode 100644 index 00000000..eaddf3b3 --- /dev/null +++ b/include/reflect/struct/StructMember.hpp @@ -0,0 +1,236 @@ +/* @file StructMember.hpp */ + +#pragma once + +#include "reflect/TypeDescr.hpp" +#include "reflect/EstablishTypeDescr.hpp" +//#include "reflect/Reflect.hpp" +#include "reflect/TaggedPtr.hpp" +#include +#include + +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(); + */ + 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 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 + 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()}, + memptr_{memptr} {} + GeneralStructMemberAccessor(GeneralStructMemberAccessor const & x) = default; + virtual ~GeneralStructMemberAccessor() = default; + + static std::unique_ptr make(Memptr memptr) { + return std::unique_ptr(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(); } + + virtual TypeDescr member_td() const override { return this->member_td_; } + + virtual void * address(void * struct_addr) const override { + return this->address_impl(reinterpret_cast(struct_addr)); + } /*address*/ + + virtual std::unique_ptr clone() const override { + return std::unique_ptr + (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::make(&Foo::x_); + * + * or equivalently: + * auto foo_x_access = GeneralStructMemberAccessor::make(&Foo::x_); + * auto bar_x_access = AncestorStructMemberAccessor::adopt(foo_x_access); + * + * can use the 2nd form to adopt accessors from an already-reflected ancestor class + * + * Require: + * - StructT -isa-> AncestorT + */ + template + class AncestorStructMemberAccessor : public AbstractStructMemberAccessor { + public: + AncestorStructMemberAccessor(std::unique_ptr ancestor_accessor) + : ancestor_accessor_{std::move(ancestor_accessor)} {} + AncestorStructMemberAccessor(AncestorStructMemberAccessor const & x) = default; + virtual ~AncestorStructMemberAccessor() = default; + + static std::unique_ptr + adopt(std::unique_ptr ancestor_accessor) { + return std::unique_ptr + (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(struct_addr); + + return this->ancestor_accessor_->member_tp(ancestor_addr); + } /*member_tp*/ +#endif + + virtual TypeDescr struct_td() const override { return EstablishTypeDescr::establish(); } + 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(struct_addr)); + } + + virtual std::unique_ptr clone() const override { + return std::unique_ptr + (new AncestorStructMemberAccessor(std::move(this->ancestor_accessor_->clone()))); + } /*clone*/ + + private: + /* .ancestor_accessor fetches some particular member of AncestorT */ + std::unique_ptr 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 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 + StructMember for_descendant() const { + assert(EstablishTypeDescr::establish() == this->get_struct_td()); + + return StructMember(this->member_name(), + std::move(AncestorStructMemberAccessor::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 accessor_; + }; /*StructMember*/ +} /*namespace reflect*/ +} /*namespace xo*/ + +/* end StructMember.hpp */ diff --git a/include/reflect/struct/StructTdx.hpp b/include/reflect/struct/StructTdx.hpp new file mode 100644 index 00000000..5e170ef7 --- /dev/null +++ b/include/reflect/struct/StructTdx.hpp @@ -0,0 +1,94 @@ +/* @file StructTdx.hpp */ + +#pragma once + +#include "reflect/TypeDescrExtra.hpp" +#include "reflect/TaggedPtr.hpp" +#include "reflect/struct/StructMember.hpp" +#include +#include +#include + +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 make(std::vector member_v, + bool have_to_self_tp, + std::function to_self_tp); + + /* specialization for std::pair + * coordinates with [reflect/Reflect.hpp] + */ + template + static std::unique_ptr pair() { + using struct_t = std::pair; + + std::vector mv; + { + auto lhs_access + (GeneralStructMemberAccessor::make + (&struct_t::first)); + + mv.push_back(StructMember("first", std::move(lhs_access))); + } + { + auto rhs_access + (GeneralStructMemberAccessor::make + (&struct_t::second)); + + mv.push_back(StructMember("second", std::move(rhs_access))); + } + + std::function null_to_self_tp; + + return make(std::move(mv), + false /*!have_to_self_tp*/, + null_to_self_tp); + } /*pair*/ + + // ----- Inherited from TypeDescrExtra ----- + + virtual Metatype metatype() const override { return Metatype::mt_struct; } + virtual TaggedPtr most_derived_self_tp(TypeDescrBase const * object_td, + void * object) const override { + if (this->have_to_self_tp_) { + return this->to_self_tp_(object); + } else { + return TypeDescrExtra::most_derived_self_tp(object_td, object); + } + } + virtual uint32_t n_child(void * /*object*/) const override { return this->member_v_.size(); } + virtual TaggedPtr child_tp(uint32_t i, void * object) const override; + virtual std::string const & struct_member_name(uint32_t i) const override; + virtual StructMember const * struct_member(uint32_t i) const override; + + private: + StructTdx(std::vector member_v, + bool have_to_self_tp, + std::function 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 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 to_self_tp_; + }; /*StructTdx*/ + + } /*namespace reflect*/ +} /*namespace xo*/ + +/* end StructTdx.hpp */ diff --git a/include/reflect/vector/VectorTdx.hpp b/include/reflect/vector/VectorTdx.hpp new file mode 100644 index 00000000..4f3309c2 --- /dev/null +++ b/include/reflect/vector/VectorTdx.hpp @@ -0,0 +1,100 @@ +/* file VectorTdx.hpp + * + * author: Roland Conybeare, Aug 2022 + */ + +#pragma once + +#include "reflect/TypeDescrExtra.hpp" +//#include "reflect/TaggedPtr.hpp" +#include "reflect/EstablishTypeDescr.hpp" +//#include "reflect/TaggedPtr.hpp" +//#include +//#include + +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 make(); + + // ----- Inherited from TypeDescrExtra ----- + + virtual Metatype metatype() const override { return Metatype::mt_vector; } + virtual uint32_t n_child(void * object) const override = 0; + virtual TaggedPtr child_tp(uint32_t i, void * object) const override = 0; + /* (forbidden) */ + virtual std::string const & struct_member_name(uint32_t i) const override; + }; /*VectorTdx*/ + + // ----- StlVectorTdx ----- + + /* require: + * - VectorT.size() + * - VectorT[int] :: lvalue + */ + template + class StlVectorTdx : public VectorTdx { + public: + using target_t = VectorT; + + static std::unique_ptr make() { + return std::unique_ptr(new StlVectorTdx()); + } /*make*/ + + virtual uint32_t n_child(void * object) const override { + target_t * vec = reinterpret_cast(object); + + return vec->size(); + } /*n_child*/ + + virtual TaggedPtr child_tp(uint32_t i, void * object) const override { + target_t * vec = reinterpret_cast(object); + + return establish_most_derived_tp(&((*vec)[i])); + } /*child_tp*/ + }; /*StlVectorTdx*/ + + // ----- std::array ----- + + /* coordinates with EstablishTdx>::make(), + * see [reflect/Reflect.hpp] + */ + + template + using StdArrayTdx = StlVectorTdx>; + + // ----- std::vector ----- + + /* coordinates with EstablishTdx>::make() + * see [reflect/Reflect.hpp] + */ + template + class StdVectorTdx : public VectorTdx { + public: + using target_t = std::vector; + + static std::unique_ptr make() { + return std::unique_ptr(new StdVectorTdx()); + } /*make*/ + + virtual uint32_t n_child(void * object) const override { + target_t * vec = reinterpret_cast(object); + + return vec->size(); + } /*n_child*/ + + virtual TaggedPtr child_tp(uint32_t i, void * object) const override { + target_t * vec = reinterpret_cast(object); + + return establish_most_derived_tp(&((*vec)[i])); + } + }; /*StdVectorTdx*/ + } /*namespace reflect*/ + +} /*namespace xo*/ + +/* end VectorTdx.hpp */ diff --git a/repo/README.md b/repo/README.md new file mode 100644 index 00000000..3acdcde4 --- /dev/null +++ b/repo/README.md @@ -0,0 +1,5 @@ + +``` +cd reflect/repo +git submodule add git@github.com/someusername/someproject.git +``` diff --git a/src/reflect/CMakeLists.txt b/src/reflect/CMakeLists.txt new file mode 100644 index 00000000..e1c1f65f --- /dev/null +++ b/src/reflect/CMakeLists.txt @@ -0,0 +1,52 @@ +# reflect/CMakeLists.txt + +set(SELF_LIBRARY_NAME reflect) +set(SELF_SOURCE_FILES TypeDescr.cpp TypeDescrExtra.cpp TaggedRcptr.cpp atomic/AtomicTdx.cpp pointer/PointerTdx.cpp vector/VectorTdx.cpp struct/StructTdx.cpp struct/StructMember.cpp init_reflect.cpp) + +# build shared library 'reflect' +add_library(${SELF_LIBRARY_NAME} SHARED ${SELF_SOURCE_FILES}) + +set_target_properties(${SELF_LIBRARY_NAME} + PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION 1) + +# ---------------------------------------------------------------- +# 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}) +xo_install_library(${SELF_LIBRARY_NAME}) + +# ---------------------------------------------------------------- +# dependencies: logutil, ... + +#xo_refcnt_dependency(${SELF_LIBRARY_NAME}) +#xo_indentlog_dependency(${SELF_LIBRARY_NAME}) + +add_dependencies(${SELF_LIBRARY_NAME} refcnt) +target_include_directories( + ${SELF_LIBRARY_NAME} PUBLIC + $ + $ +) +target_link_libraries(${SELF_LIBRARY_NAME} PUBLIC refcnt) + +add_dependencies(${SELF_LIBRARY_NAME} indentlog) +# note: can't use find_package() here, +# because find_package() needs to run successfully before +# dependency gets installed. +target_include_directories( + ${SELF_LIBRARY_NAME} PUBLIC + $ + $ +) +target_link_libraries(${SELF_LIBRARY_NAME} PUBLIC indentlog) + +# ---------------------------------------------------------------- +# 3rd party dependency: boost: + +#xo_boost_dependency(${SELF_LIBRARY_NAME}) + +# end CMakeLists.txt diff --git a/src/reflect/TaggedRcptr.cpp b/src/reflect/TaggedRcptr.cpp new file mode 100644 index 00000000..cc461e0c --- /dev/null +++ b/src/reflect/TaggedRcptr.cpp @@ -0,0 +1,30 @@ +/* file TaggedRcptr.cpp + * + * author: Roland Conybeare, Aug 2022 + */ + +#include "TaggedRcptr.hpp" +#include "indentlog/print/tag.hpp" + +namespace xo { + using xo::xtag; + using xo::tostr; + + namespace reflect { + void + TaggedRcptr::display(std::ostream & os) const + { + os << "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 */ diff --git a/src/reflect/TypeDescr.cpp b/src/reflect/TypeDescr.cpp new file mode 100644 index 00000000..564fec14 --- /dev/null +++ b/src/reflect/TypeDescr.cpp @@ -0,0 +1,194 @@ +/* @file TypeDescr.cpp */ + +#include "TypeDescr.hpp" +#include "TaggedPtr.hpp" +#include "TypeDescrExtra.hpp" +#include "atomic/AtomicTdx.hpp" +#include "indentlog/scope.hpp" + +namespace xo { + using xo::scope; + using xo::xtag; + using xo::tostr; + + namespace reflect { + uint32_t + TypeId::s_next_id = 1; + + std::unordered_map> + TypeDescrBase::s_type_table_map; + + std::unordered_map + TypeDescrBase::s_coalesced_type_table_map; + + std::vector + TypeDescrBase::s_type_table_v; + + TypeDescrW + TypeDescrBase::require(std::type_info const * tinfo, + std::string_view canonical_name, + std::unique_ptr tdextra) + { + /* 1. lookup by tinfo hash_code in s_type_table_map */ + { + auto ix = s_type_table_map.find(TypeInfoRef(tinfo)); + + if ((ix != s_type_table_map.end()) && ix->second) + return ix->second.get(); + } + + /* 2. lookup by tinfo hash_code in s_coalesced_type_table_map */ + { + auto ix = s_coalesced_type_table_map.find(TypeInfoRef(tinfo)); + + if ((ix != s_coalesced_type_table_map.end()) && ix->second) + return ix->second; + } + + /* 3. O(n) lookup by canonical_name, before we create a new slot. + * + * Have to accept that on clang type_info objects aren't always unique (!$@#!!) + * + * TODO: lookup table keyed by canonical_name + */ + for (TypeDescrBase * x : s_type_table_v) { + if (x && (x->canonical_name() == canonical_name)) { + /* 1. assume *x represents the type associated with tinfo. + * 2. *do* store tinfo in s_coalesced_type_table_map[], + * for faster lookup next time + */ + s_coalesced_type_table_map[TypeInfoRef(tinfo)] = x; + + return x; + } + } + + TypeId id = TypeId::allocate(); + + std::unique_ptr & slot = s_type_table_map[TypeInfoRef(tinfo)]; + + slot.reset(new TypeDescrBase(id, + tinfo, + canonical_name, + std::move(tdextra))); + + if (s_type_table_v.size() <= id.id()) + s_type_table_v.resize(id.id() + 1); + + s_type_table_v[id.id()] = slot.get(); + + return slot.get(); + } /*require*/ + + void + TypeDescrBase::print_reflected_types(std::ostream & os) + { + os << "display(os); + } + } + + os << ">\n"; + } /*print_reflected_types*/ + + namespace { + /* readability hack: + * foo::bar::Quux ==> Quux + * but lookout for template names: + * std::pair ==> pair + */ + 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, + std::type_info const * tinfo, + std::string_view canonical_name, + std::unique_ptr tdextra) + : id_{std::move(id)}, + typeinfo_{tinfo}, + canonical_name_{std::move(canonical_name)}, + short_name_{unqualified_name(canonical_name_)}, + 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 << "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*/ + + void + TypeDescrBase::assign_tdextra(std::unique_ptr 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*/ + } /*namespace reflect*/ +} /*namespace xo*/ + +/* end TypeDescr.cpp */ diff --git a/src/reflect/TypeDescrExtra.cpp b/src/reflect/TypeDescrExtra.cpp new file mode 100644 index 00000000..e53a216b --- /dev/null +++ b/src/reflect/TypeDescrExtra.cpp @@ -0,0 +1,37 @@ +/* file TypeDescrExtra.cpp + * + * author: Roland Conybeare, Aug 2022 + */ + +#include "TypeDescrExtra.hpp" +#include "TypeDescr.hpp" +#include "TaggedPtr.hpp" +#include + +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 */ diff --git a/src/reflect/atomic/AtomicTdx.cpp b/src/reflect/atomic/AtomicTdx.cpp new file mode 100644 index 00000000..42ca9e2f --- /dev/null +++ b/src/reflect/atomic/AtomicTdx.cpp @@ -0,0 +1,24 @@ +/* @file AtomicTdx.cpp */ + +#include "atomic/AtomicTdx.hpp" +#include "TaggedPtr.hpp" + +namespace xo { + namespace reflect { + std::unique_ptr AtomicTdx::make() { + return std::unique_ptr(new AtomicTdx()); + } /*make*/ + + TaggedPtr + AtomicTdx::child_tp(uint32_t /*i*/, void * /*object*/) const { + return TaggedPtr::universal_null(); + } /*child_tp*/ + + 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 */ diff --git a/src/reflect/init_reflect.cpp b/src/reflect/init_reflect.cpp new file mode 100644 index 00000000..20dd1441 --- /dev/null +++ b/src/reflect/init_reflect.cpp @@ -0,0 +1,23 @@ +/* file init_reflect.cpp + * + * author: Roland Conybeare, Sep 2022 + */ + +#include "init_reflect.hpp" +#include "subsys/Subsystem.hpp" + +namespace xo { + void + InitSubsys::init() + { + /* placeholder -- expecting there to be non-trivial content soon */ + } /*init*/ + + InitEvidence + InitSubsys::require() + { + return Subsystem::provide("reflect", &init); + } /*require*/ +} /*namespace xo*/ + +/* end init_reflect.cpp */ diff --git a/src/reflect/pointer/PointerTdx.cpp b/src/reflect/pointer/PointerTdx.cpp new file mode 100644 index 00000000..cc81e391 --- /dev/null +++ b/src/reflect/pointer/PointerTdx.cpp @@ -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 */ diff --git a/src/reflect/struct/StructMember.cpp b/src/reflect/struct/StructMember.cpp new file mode 100644 index 00000000..59d69056 --- /dev/null +++ b/src/reflect/struct/StructMember.cpp @@ -0,0 +1,36 @@ +/* file StructMember.cpp + * + * author: Roland Conybeare, Aug 2022 + */ + +#include "struct/StructMember.hpp" +#include "indentlog/scope.hpp" +#include + +namespace xo { + using xo::scope; + using xo::xtag; + + namespace reflect { + static_assert(std::is_move_constructible_v); + + 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 */ diff --git a/src/reflect/struct/StructTdx.cpp b/src/reflect/struct/StructTdx.cpp new file mode 100644 index 00000000..593ad388 --- /dev/null +++ b/src/reflect/struct/StructTdx.cpp @@ -0,0 +1,55 @@ +/* @file StructTdx.cpp */ + +#include "struct/StructTdx.hpp" + +namespace xo { + using std::uint32_t; + + namespace reflect { + std::unique_ptr + StructTdx::make(std::vector member_v, + bool have_to_self_tp, + std::function to_self_tp) + { + return std::unique_ptr(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(); + } + + StructMember const & member_info = this->member_v_[i]; + + return member_info.get_member_tp(object); + + } /*get_child*/ + + 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 */ diff --git a/src/reflect/vector/VectorTdx.cpp b/src/reflect/vector/VectorTdx.cpp new file mode 100644 index 00000000..fd2e40e4 --- /dev/null +++ b/src/reflect/vector/VectorTdx.cpp @@ -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 */ diff --git a/utest/CMakeLists.txt b/utest/CMakeLists.txt new file mode 100644 index 00000000..80ce49ac --- /dev/null +++ b/utest/CMakeLists.txt @@ -0,0 +1,57 @@ +# 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) + +add_executable(${SELF_EXECUTABLE_NAME} ${SELF_SOURCE_FILES}) +xo_include_options(${SELF_EXECUTABLE_NAME}) + +add_test(NAME ${SELF_EXECUTABLE_NAME} COMMAND ${SELF_EXECUTABLE_NAME}) +target_code_coverage(${SELF_EXECUTABLE_NAME} AUTO ALL) + +# ---------------------------------------------------------------- +# generic project dependency + +# PROJECT_SOURCE_DIR: +# so we can for example write +# #include "indentlog/scope.hpp" +# from anywhere in the project +# PROJECT_BINARY_DIR: +# since version file will be in build directory, need that directory +# to also be included in compiler's include path +# +target_include_directories(${SELF_EXECUTABLE_NAME} PUBLIC + ${PROJECT_SOURCE_DIR} + ${PROJECT_BINARY_DIR}) + +# ---------------------------------------------------------------- +# internal dependencies: logutil, ... + +target_link_libraries(${SELF_EXECUTABLE_NAME} PUBLIC reflect) + +# ---------------------------------------------------------------- +# 3rd part dependency: catch2: + +find_package(Catch2 2 REQUIRED) + +# need this so that catch2/include appears in compile_commands.json, +# on which lsp integration relies. +# +# See also /nix/store/*-catch2-*/lib/cmake/Catch2/ParseAndAddCatchTests.cmake; +# commands here derived from ^ .cmake file +# +#find_path(CATCH_INCLUDE_DIR "catch2/catch.hpp") +#target_include_directories(${SELF_EXECUTABLE_NAME} PUBLIC ${CATCH_INCLUDE_DIR}) + +# ---------------------------------------------------------------- +# make standard directories for std:: includes explicit +# so that +# (1) they appear in compile_commands.json. +# (2) clangd (run from emacs lsp-mode) can find them +# +if(CMAKE_EXPORT_COMPILE_COMMANDS) + set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES + ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) +endif() + +# end CMakeLists.txt diff --git a/utest/StructReflector.test.cpp b/utest/StructReflector.test.cpp new file mode 100644 index 00000000..5bf87ade --- /dev/null +++ b/utest/StructReflector.test.cpp @@ -0,0 +1,142 @@ +/* file StructReflector.test.cpp + * + * author: Roland Conybeare, Aug 2022 + */ + +#include "reflect/Reflect.hpp" +#include "reflect/StructReflector.hpp" +#include + +#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 sr; + + REQUIRE(Reflect::is_reflected() == false); + REQUIRE(Reflect::is_reflected() == true); + + TestStruct0 recd0; + TaggedPtr tp = Reflect::make_tp(&recd0); + + REQUIRE(tp.address() == &recd0); + REQUIRE(tp.td() == Reflect::require()); + + 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 sr; + + REQUIRE(Reflect::is_reflected() == true); + + //sr.reflect_member(STRINGIFY(x_), &decltype(sr)::struct_t::x_); + REFLECT_LITERAL_MEMBER(sr, x_); + + REQUIRE(!Reflect::require()->is_struct()); + + sr.require_complete(); + + REQUIRE(Reflect::require()->is_struct()); + } /*TEST_CASE(struct-reflect-s1)*/ + + namespace { + struct TestStructS2 { int x_; }; + } + + TEST_CASE("struct-reflect-s2", "[reflect]") { + StructReflector sr; + + REQUIRE(Reflect::is_reflected() == true); + + //sr.reflect_member(STRINGIFY(x_), &decltype(sr)::struct_t::x_); + REFLECT_MEMBER(sr, x); + + REQUIRE(!Reflect::require()->is_struct()); + + sr.require_complete(); + + REQUIRE(Reflect::require()->is_struct()); + + TestStructS2 recd1{666}; + + TaggedPtr tp = Reflect::make_tp(&recd1); + + REQUIRE(tp.address() == &recd1); + REQUIRE(tp.td() == Reflect::require()); + + REQUIRE(tp.n_child() == 1); + + REQUIRE(tp.get_child(0).td() == Reflect::require()); + 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 sr; + + REQUIRE(Reflect::is_reflected() == true); + + REFLECT_MEMBER(sr, x); + REFLECT_MEMBER(sr, y); + REFLECT_MEMBER(sr, z); + + REQUIRE(!Reflect::require()->is_struct()); + + sr.require_complete(); + + REQUIRE(Reflect::require()->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()); + + REQUIRE(tp.n_child() == 3); + + REQUIRE(tp.get_child(0).td() == Reflect::require()); + REQUIRE(tp.get_child(0).address() == &(recd1.x_)); + + REQUIRE(tp.get_child(1).td() == Reflect::require()); + REQUIRE(tp.get_child(1).address() == &(recd1.y_)); + + REQUIRE(tp.get_child(2).td() == Reflect::require()); + 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 */ diff --git a/utest/StructTdx.test.cpp b/utest/StructTdx.test.cpp new file mode 100644 index 00000000..c20e2139 --- /dev/null +++ b/utest/StructTdx.test.cpp @@ -0,0 +1,59 @@ +/* file StructTdx.test.cpp + * + * author: Roland Conybeare, Aug 2022 + */ + +#include "reflect/Reflect.hpp" +#include + +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 p; + + TaggedPtr tp = Reflect::make_tp(&p); + //TypeDescr td = Reflect::require>(); + + REQUIRE(Reflect::is_reflected>() == 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>() == &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() == &(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() == &(p.second)); + REQUIRE(tp1.n_child() == 0); + + } /*TEST_CASE(std-pair-reflect)*/ + + } /*namespace ut*/ +} /*namespace xo*/ + +/* end VectorTdx.test.cpp */ diff --git a/utest/VectorTdx.test.cpp b/utest/VectorTdx.test.cpp new file mode 100644 index 00000000..3836b4f7 --- /dev/null +++ b/utest/VectorTdx.test.cpp @@ -0,0 +1,181 @@ +/* file VectorTdx.test.cpp + * + * author: Roland Conybeare, Aug 2022 + */ + +#include "reflect/Reflect.hpp" +#include + +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 v; + + TaggedPtr tp = Reflect::make_tp(&v); + //TypeDescr td = Reflect::require>(); + + REQUIRE(Reflect::is_reflected>() == 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>() == &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 v = { 1.123 }; + + TaggedPtr tp = Reflect::make_tp(&v); + + REQUIRE(Reflect::is_reflected>() == 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>() == &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() == &(v[0])); + REQUIRE(tp0.n_child() == 0); + } /*TEST_CASE(std-vector-reflect-one)*/ + + TEST_CASE("std-vector-reflect-two", "[reflect]") { + std::vector v = { 1.123, 2.234 }; + + TaggedPtr tp = Reflect::make_tp(&v); + + REQUIRE(Reflect::is_reflected>() == 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>() == &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() == &(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() == &(v[1])); + REQUIRE(tp1.n_child() == 0); + } /*TEST(std-vector-reflect-two)*/ + + // ----- std::array ----- + + TEST_CASE("std-array-reflect-empty", "[reflect]") { + std::array v; + + TaggedPtr tp = Reflect::make_tp(&v); + //TypeDescr td = Reflect::require>(); + + REQUIRE(Reflect::is_reflected>() == 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>() == &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 v = { 1.123 }; + + TaggedPtr tp = Reflect::make_tp(&v); + + REQUIRE(Reflect::is_reflected>() == 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>() == &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() == &(v[0])); + REQUIRE(tp0.n_child() == 0); + } /*TEST_CASE(std-array-reflect-one)*/ + + TEST_CASE("std-array-reflect-two", "[reflect]") { + std::array v = { 1.123, 2.234 }; + + TaggedPtr tp = Reflect::make_tp(&v); + + REQUIRE(Reflect::is_reflected>() == 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>() == &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() == &(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() == &(v[1])); + REQUIRE(tp1.n_child() == 0); + } /*TEST(std-array-reflect-two)*/ + + } /*namespace ut*/ +} /*namespace xo*/ + +/* end VectorTdx.test.cpp */ diff --git a/utest/reflect_utest_main.cpp b/utest/reflect_utest_main.cpp new file mode 100644 index 00000000..91c62d09 --- /dev/null +++ b/utest/reflect_utest_main.cpp @@ -0,0 +1,6 @@ +/* file reflect_utest_main.cpp */ + +#define CATCH_CONFIG_MAIN +#include "catch2/catch.hpp" + +/* end reflect_utest_main.cpp */ From ab23f431af88de666c69912f76e88514bffbab91 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 25 Sep 2023 17:55:04 -0400 Subject: [PATCH 005/111] + build/install notes --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 27fe0373..fb6682d4 100644 --- a/README.md +++ b/README.md @@ -1 +1,19 @@ # reflection library + +### build + install +``` +$ cd reflect +$ mkdir build +$ cd build +$ cmake -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} .. +$ make +$ make install +``` + +### build for unit test coverage +``` +$ cd refcnt +$ mkdir build-ccov +$ cd build-ccov +$ cmake -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} -DCODE_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug .. +``` From 57808cc6142a237e65d2f9893364dc8a3db9a2c3 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 25 Sep 2023 17:56:57 -0400 Subject: [PATCH 006/111] + .gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..6637741b --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +# clangd working space (see emacs+lsp) +.cache +# typical cmake build directory (source-tree-nephew) +build*/* From 4cc5f4049fc96cdf96b2aabda438bbea5bb95900 Mon Sep 17 00:00:00 2001 From: Roland Date: Mon, 25 Sep 2023 17:59:47 -0400 Subject: [PATCH 007/111] Create cmake-single-platform.yml --- .github/workflows/cmake-single-platform.yml | 39 +++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/cmake-single-platform.yml diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml new file mode 100644 index 00000000..28c6f783 --- /dev/null +++ b/.github/workflows/cmake-single-platform.yml @@ -0,0 +1,39 @@ +# This starter workflow is for a CMake project running on a single platform. There is a different starter workflow if you need cross-platform coverage. +# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-multi-platform.yml +name: CMake on a single platform + +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: + - uses: actions/checkout@v3 + + - name: Configure CMake + # 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 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + + - name: Build + # Build your program with the given configuration + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + + - name: Test + working-directory: ${{github.workspace}}/build + # 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}} + From 94788b53f0a14d3389bc9ba88dcd45402f812cbe Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 25 Sep 2023 18:05:44 -0400 Subject: [PATCH 008/111] github: action for git submodules, take 1 --- .github/workflows/cmake-single-platform.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index 28c6f783..a0fdcdc6 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -22,6 +22,10 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Update submodules + # update/fetch submodules (attached as top-level children of reflect/repo/) + run: git submodule update --init --recursive + - name: Configure CMake # 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 @@ -36,4 +40,3 @@ jobs: # 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}} - From 31e0c14930b4643ef7021ea3f993951e175aa1ed Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 25 Sep 2023 18:14:10 -0400 Subject: [PATCH 009/111] github: submodule actions, take 2 --- .github/workflows/cmake-single-platform.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index a0fdcdc6..8cda1cd7 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -20,11 +20,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - - name: Update submodules - # update/fetch submodules (attached as top-level children of reflect/repo/) - run: git submodule update --init --recursive + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: true - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. From d1833c5fa60c76d380ce1a914bf8dd5ec863a955 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 25 Sep 2023 18:16:20 -0400 Subject: [PATCH 010/111] github: apt-get catch2 dependency --- .github/workflows/cmake-single-platform.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index 8cda1cd7..663a266b 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -20,6 +20,9 @@ jobs: runs-on: ubuntu-latest steps: + - name: Install catch2 + run: sudo apt-get install -y catch2 + - name: Checkout uses: actions/checkout@v3 with: From 8f36e73b18c22ee4cd57caaa985360213e006ebe Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 01:27:13 -0400 Subject: [PATCH 011/111] build: remove submodules (moved to reflect-sm project) --- .gitmodules | 9 ---- CMakeLists.txt | 85 -------------------------------------- repo/README.md | 5 --- repo/refcnt | 1 - repo/subsys | 1 - src/reflect/CMakeLists.txt | 6 ++- 6 files changed, 4 insertions(+), 103 deletions(-) delete mode 100644 .gitmodules delete mode 100644 repo/README.md delete mode 160000 repo/refcnt delete mode 160000 repo/subsys diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index ebcf3a27..00000000 --- a/.gitmodules +++ /dev/null @@ -1,9 +0,0 @@ -[submodule "repo/indentlog"] - path = repo/indentlog - url = git@github.com:Rconybea/indentlog.git -[submodule "repo/refcnt"] - path = repo/refcnt - url = git@github.com:Rconybea/refcnt.git -[submodule "repo/subsys"] - path = repo/subsys - url = git@github.com:Rconybea/subsys.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f777c59..078681e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,91 +41,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED True) # always write compile_commands.json set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "") -# ---------------------------------------------------------------- -# external projects (need these to exist before add_subdirectory() below) -# -# we are expecting these projects to coexist peacefully in build/local -# (i.e. can run their `make install` steps independently with prefix build/local, -# without any collisions) -# - -include(ExternalProject) - -## ----- indentlog ------ - -# NOTE: we could have cmake handle git interaction, -# but we want source for certain dependencies to live in a location -# that's suitable for accepting changes + coordinated commits. -# In particular, not in the build directory! -# -externalproject_add( - project_indentlog - SOURCE_DIR ${PROJECT_SOURCE_DIR}/repo/indentlog - BINARY_DIR ${PROJECT_BINARY_DIR}/ext/indentlog - INSTALL_DIR ${PROJECT_BINARY_DIR}/local - CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCODE_COVERAGE=${CODE_COVERAGE} -DCMAKE_PREFIX_PATH= -DCMAKE_INSTALL_PREFIX= - BUILD_COMMAND make - INSTALL_COMMAND make install - TEST_BEFORE_INSTALL True -) - -add_library(indentlog INTERFACE IMPORTED) -#set_property(TARGET indentlog PROPERTY IMPORTED_LOCATION ${PROJECT_BINARY_DIR}/local/lib/libindentlog.so) -add_dependencies(indentlog project_indentlog) - -# runs ctest in indentlog build dir -add_test(NAME indentlog COMMAND ${PROJECT_SOURCE_DIR}/cmake/run-external-ctest ${PROJECT_BINARY_DIR}/ext/indentlog) -#target_code_coverage(indentlog EXTERNAL AUTO ALL) - -# ----- subsys ----- - -externalproject_add( - project_subsys - SOURCE_DIR ${PROJECT_SOURCE_DIR}/repo/subsys - BINARY_DIR ${PROJECT_BINARY_DIR}/ext/subsys - INSTALL_DIR ${PROJECT_BINARY_DIR}/local - CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCODE_COVERAGE=${CODE_COVERAGE} -DCMAKE_PREFIX_PATH= -DCMAKE_INSTALL_PREFIX= - BUILD_COMMAND make - INSTALL_COMMAND make install - TEST_BEFORE_INSTALL True -) - -add_library(subsys INTERFACE IMPORTED) -add_dependencies(subsys project_subsys) - -# runs ctest in subsys build dir -add_test(NAME subsys COMMAND ${PROJECT_SOURCE_DIR}/cmake/run-external-ctest ${PROJECT_BINARY_DIR}/ext/subsys) - -# ----- refcnt ----- - -# CMAKE_ARGS -# CMAKE_BUILD_TYPE propagate Debug/Release build type -# CODE_COVERAGE propagate code coverage setting -# CMAKE_PREFIX_PATH path for support cmake files of dependencies (needed for find_package() to work) -# CMAKE_INSTALL_PREFIX install subproject here -# SOURCE_DIR -- where to find already established source code -# BINARY_DIR -- run build for external project here -# INSTALL_DIR -- (temporarily) install external project here -# -externalproject_add( - project_refcnt - SOURCE_DIR ${PROJECT_SOURCE_DIR}/repo/refcnt - BINARY_DIR ${PROJECT_BINARY_DIR}/ext/refcnt - INSTALL_DIR ${PROJECT_BINARY_DIR}/local - CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCODE_COVERAGE=${CODE_COVERAGE} -DCMAKE_PREFIX_PATH= -DCMAKE_INSTALL_PREFIX= - BUILD_COMMAND make - INSTALL_COMMAND make install - TEST_BEFORE_INSTALL True -) - -add_library(refcnt SHARED IMPORTED) -set_property(TARGET refcnt PROPERTY IMPORTED_LOCATION ${PROJECT_BINARY_DIR}/local/lib/librefcnt.so) -add_dependencies(refcnt project_refcnt) -add_dependencies(refcnt project_indentlog) - -# runs ctest in refcnt build dir -add_test(NAME refcnt COMMAND ${PROJECT_SOURCE_DIR}/cmake/run-external-ctest ${PROJECT_BINARY_DIR}/ext/refcnt) - # ---------------------------------------------------------------- # sources diff --git a/repo/README.md b/repo/README.md deleted file mode 100644 index 3acdcde4..00000000 --- a/repo/README.md +++ /dev/null @@ -1,5 +0,0 @@ - -``` -cd reflect/repo -git submodule add git@github.com/someusername/someproject.git -``` diff --git a/repo/refcnt b/repo/refcnt deleted file mode 160000 index 4b3c854f..00000000 --- a/repo/refcnt +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4b3c854fc7872ba404ce4e0af25ecfdc47822ae1 diff --git a/repo/subsys b/repo/subsys deleted file mode 160000 index 1c384e1f..00000000 --- a/repo/subsys +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1c384e1ff0a01885bdf0713be858085814b6ece6 diff --git a/src/reflect/CMakeLists.txt b/src/reflect/CMakeLists.txt index e1c1f65f..799cd10f 100644 --- a/src/reflect/CMakeLists.txt +++ b/src/reflect/CMakeLists.txt @@ -25,7 +25,8 @@ xo_install_library(${SELF_LIBRARY_NAME}) #xo_refcnt_dependency(${SELF_LIBRARY_NAME}) #xo_indentlog_dependency(${SELF_LIBRARY_NAME}) -add_dependencies(${SELF_LIBRARY_NAME} refcnt) +find_package(refcnt REQUIRED) +#add_dependencies(${SELF_LIBRARY_NAME} refcnt) target_include_directories( ${SELF_LIBRARY_NAME} PUBLIC $ @@ -33,7 +34,8 @@ target_include_directories( ) target_link_libraries(${SELF_LIBRARY_NAME} PUBLIC refcnt) -add_dependencies(${SELF_LIBRARY_NAME} indentlog) +find_package(indentlog REQUIRED) +#add_dependencies(${SELF_LIBRARY_NAME} indentlog) # note: can't use find_package() here, # because find_package() needs to run successfully before # dependency gets installed. From 129e762c7b6ea783425544fdfcd8bb52800f0112 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 01:32:50 -0400 Subject: [PATCH 012/111] build: + CMAKE_INSTALL_RPATH + remov externalproject_add() calls --- CMakeLists.txt | 87 ++------------------------------------------------ 1 file changed, 3 insertions(+), 84 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f777c59..dc1646f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,90 +41,9 @@ set(CMAKE_CXX_STANDARD_REQUIRED True) # always write compile_commands.json set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "") -# ---------------------------------------------------------------- -# external projects (need these to exist before add_subdirectory() below) -# -# we are expecting these projects to coexist peacefully in build/local -# (i.e. can run their `make install` steps independently with prefix build/local, -# without any collisions) -# - -include(ExternalProject) - -## ----- indentlog ------ - -# NOTE: we could have cmake handle git interaction, -# but we want source for certain dependencies to live in a location -# that's suitable for accepting changes + coordinated commits. -# In particular, not in the build directory! -# -externalproject_add( - project_indentlog - SOURCE_DIR ${PROJECT_SOURCE_DIR}/repo/indentlog - BINARY_DIR ${PROJECT_BINARY_DIR}/ext/indentlog - INSTALL_DIR ${PROJECT_BINARY_DIR}/local - CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCODE_COVERAGE=${CODE_COVERAGE} -DCMAKE_PREFIX_PATH= -DCMAKE_INSTALL_PREFIX= - BUILD_COMMAND make - INSTALL_COMMAND make install - TEST_BEFORE_INSTALL True -) - -add_library(indentlog INTERFACE IMPORTED) -#set_property(TARGET indentlog PROPERTY IMPORTED_LOCATION ${PROJECT_BINARY_DIR}/local/lib/libindentlog.so) -add_dependencies(indentlog project_indentlog) - -# runs ctest in indentlog build dir -add_test(NAME indentlog COMMAND ${PROJECT_SOURCE_DIR}/cmake/run-external-ctest ${PROJECT_BINARY_DIR}/ext/indentlog) -#target_code_coverage(indentlog EXTERNAL AUTO ALL) - -# ----- subsys ----- - -externalproject_add( - project_subsys - SOURCE_DIR ${PROJECT_SOURCE_DIR}/repo/subsys - BINARY_DIR ${PROJECT_BINARY_DIR}/ext/subsys - INSTALL_DIR ${PROJECT_BINARY_DIR}/local - CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCODE_COVERAGE=${CODE_COVERAGE} -DCMAKE_PREFIX_PATH= -DCMAKE_INSTALL_PREFIX= - BUILD_COMMAND make - INSTALL_COMMAND make install - TEST_BEFORE_INSTALL True -) - -add_library(subsys INTERFACE IMPORTED) -add_dependencies(subsys project_subsys) - -# runs ctest in subsys build dir -add_test(NAME subsys COMMAND ${PROJECT_SOURCE_DIR}/cmake/run-external-ctest ${PROJECT_BINARY_DIR}/ext/subsys) - -# ----- refcnt ----- - -# CMAKE_ARGS -# CMAKE_BUILD_TYPE propagate Debug/Release build type -# CODE_COVERAGE propagate code coverage setting -# CMAKE_PREFIX_PATH path for support cmake files of dependencies (needed for find_package() to work) -# CMAKE_INSTALL_PREFIX install subproject here -# SOURCE_DIR -- where to find already established source code -# BINARY_DIR -- run build for external project here -# INSTALL_DIR -- (temporarily) install external project here -# -externalproject_add( - project_refcnt - SOURCE_DIR ${PROJECT_SOURCE_DIR}/repo/refcnt - BINARY_DIR ${PROJECT_BINARY_DIR}/ext/refcnt - INSTALL_DIR ${PROJECT_BINARY_DIR}/local - CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCODE_COVERAGE=${CODE_COVERAGE} -DCMAKE_PREFIX_PATH= -DCMAKE_INSTALL_PREFIX= - BUILD_COMMAND make - INSTALL_COMMAND make install - TEST_BEFORE_INSTALL True -) - -add_library(refcnt SHARED IMPORTED) -set_property(TARGET refcnt PROPERTY IMPORTED_LOCATION ${PROJECT_BINARY_DIR}/local/lib/librefcnt.so) -add_dependencies(refcnt project_refcnt) -add_dependencies(refcnt project_indentlog) - -# runs ctest in refcnt build dir -add_test(NAME refcnt COMMAND ${PROJECT_SOURCE_DIR}/cmake/run-external-ctest ${PROJECT_BINARY_DIR}/ext/refcnt) +if(NOT CMAKE_INSTALL_RPATH) + set(CMAKE_INSTALL_RPATH $(CMAKE_INSTALL_PREFIX)/lib CACHE STRING "runpath in installed libraries/executables") +endif() # ---------------------------------------------------------------- # sources From 56154d53af8d87f1e2a6f5432335fecd95a41b1c Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 01:42:18 -0400 Subject: [PATCH 013/111] build: drop repo/indentlog (was submodule w/ a preceding commit) --- repo/indentlog | 1 - 1 file changed, 1 deletion(-) delete mode 160000 repo/indentlog diff --git a/repo/indentlog b/repo/indentlog deleted file mode 160000 index bf92724a..00000000 --- a/repo/indentlog +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bf92724aa5d123b6acb9b00318e579828fb9ff38 From 06a9f72ef5c08df1b21c275fac4fc04d43f0f8b8 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 11:43:12 -0400 Subject: [PATCH 014/111] github: + checkout/build/install indentlog --- .github/workflows/cmake-single-platform.yml | 23 +++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index 663a266b..9de50c13 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -23,21 +23,36 @@ jobs: - name: Install catch2 run: sudo apt-get install -y catch2 - - name: Checkout + - name: Clone indentlog + uses: actions/checkout@v3 + with: + repository: Rconybea/indentlog + path: repo/indentlog + + - name: Configure indentlog + run: cmake -B ${{github.workspace}}/build_indentlog -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 + run: cmake --install ${{github.workspace}}/build_indentlog + + - name: Checkout reflect uses: actions/checkout@v3 with: submodules: true - - name: Configure CMake + - name: Configure 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 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} - - name: Build + - name: Build reflect # Build your program with the given configuration run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} - - name: Test + - name: Test reflect working-directory: ${{github.workspace}}/build # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail From 955462003b4cb6abf85358795dc4bab7734a796a Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 11:46:17 -0400 Subject: [PATCH 015/111] github: checkout/build/install subsys dep --- .github/workflows/cmake-single-platform.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index 9de50c13..81fbb2eb 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -38,6 +38,21 @@ jobs: - name: Install indentlog run: cmake --install ${{github.workspace}}/build_indentlog + - name: Clone subsys + uses: actions/checkout@v3 + with: + repository: Rconybea/subsys + path: repo/subsys + + - name: Configure subsys + run: cmake -B ${{github.workspace}}/build_subsys -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 + run: cmake --install ${{github.workspace}}/build_subsys + - name: Checkout reflect uses: actions/checkout@v3 with: From d1be10ab3a5be4f8aa9f890aa0f97b30083079d6 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 11:48:02 -0400 Subject: [PATCH 016/111] github: checkout/build/install refcnt dep --- .github/workflows/cmake-single-platform.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index 81fbb2eb..df05cf9b 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -53,6 +53,21 @@ jobs: - name: Install subsys run: cmake --install ${{github.workspace}}/build_subsys + - name: Clone refcnt + uses: actions/checkout@v3 + with: + repository: Rconybea/refcnt + path: repo/refcnt + + - name: Configure refcnt + run: cmake -B ${{github.workspace}}/build_refcnt -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 + run: cmake --install ${{github.workspace}}/build_refcnt + - name: Checkout reflect uses: actions/checkout@v3 with: From d7232b4da6a9c06ce24f892c65db0b5da143f6ec Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 11:52:18 -0400 Subject: [PATCH 017/111] github: fix installdir for sibling xo deps --- .github/workflows/cmake-single-platform.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index df05cf9b..b921565b 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -70,13 +70,11 @@ jobs: - name: Checkout reflect uses: actions/checkout@v3 - with: - submodules: true - name: Configure 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 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + run: cmake -B ${{github.workspace}}/build -DCMAKE_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} - name: Build reflect # Build your program with the given configuration From 5059252204a534ee8340d0e06a9beafd0b54ba1e Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 13:00:46 -0400 Subject: [PATCH 018/111] github: use specific build directory for reflect --- .github/workflows/cmake-single-platform.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index b921565b..70826978 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -23,6 +23,7 @@ jobs: - name: Install catch2 run: sudo apt-get install -y catch2 + - name: Clone indentlog uses: actions/checkout@v3 with: @@ -38,6 +39,7 @@ jobs: - name: Install indentlog run: cmake --install ${{github.workspace}}/build_indentlog + - name: Clone subsys uses: actions/checkout@v3 with: @@ -47,6 +49,7 @@ jobs: - name: Configure subsys run: cmake -B ${{github.workspace}}/build_subsys -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/subsys + - name: Build subsys run: cmake --build ${{github.workspace}}/build_subsys --config ${{env.BUILD_TYPE}} @@ -59,6 +62,7 @@ jobs: repository: Rconybea/refcnt path: repo/refcnt + - name: Configure refcnt run: cmake -B ${{github.workspace}}/build_refcnt -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/refcnt @@ -71,17 +75,19 @@ jobs: - name: Checkout reflect uses: actions/checkout@v3 + + - name: Configure 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 -DCMAKE_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + run: cmake -B ${{github.workspace}}/build_reflect -DCMAKE_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} - name: Build reflect # Build your program with the given configuration - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + run: cmake --build ${{github.workspace}}/build_reflect --config ${{env.BUILD_TYPE}} - name: Test reflect - working-directory: ${{github.workspace}}/build + 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}} From e636c59f62b47eecce81d85aaa65f3b628d385d8 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 13:04:24 -0400 Subject: [PATCH 019/111] github: try --debug-find --- .github/workflows/cmake-single-platform.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index 70826978..a9b9ac4f 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -80,7 +80,7 @@ jobs: - name: Configure 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_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + run: cmake -B ${{github.workspace}}/build_reflect -DCMAKE_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} --debug-find - name: Build reflect # Build your program with the given configuration From ce7154db57cf13456798158fad570da4fd05b830 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 13:44:21 -0400 Subject: [PATCH 020/111] build: use cmake find_package() in CONFIG mode --- src/reflect/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/reflect/CMakeLists.txt b/src/reflect/CMakeLists.txt index 799cd10f..af38df15 100644 --- a/src/reflect/CMakeLists.txt +++ b/src/reflect/CMakeLists.txt @@ -25,7 +25,7 @@ xo_install_library(${SELF_LIBRARY_NAME}) #xo_refcnt_dependency(${SELF_LIBRARY_NAME}) #xo_indentlog_dependency(${SELF_LIBRARY_NAME}) -find_package(refcnt REQUIRED) +find_package(refcnt CONFIG REQUIRED) #add_dependencies(${SELF_LIBRARY_NAME} refcnt) target_include_directories( ${SELF_LIBRARY_NAME} PUBLIC @@ -34,7 +34,7 @@ target_include_directories( ) target_link_libraries(${SELF_LIBRARY_NAME} PUBLIC refcnt) -find_package(indentlog REQUIRED) +find_package(indentlog CONFIG REQUIRED) #add_dependencies(${SELF_LIBRARY_NAME} indentlog) # note: can't use find_package() here, # because find_package() needs to run successfully before From 3d8fd35e82f6373076286aae8ae587bfc220dd6b Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 14:03:45 -0400 Subject: [PATCH 021/111] build: print cmake version --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index dc1646f0..2ddd1ad2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.10) +message(CMAKE_VERSION=${CMAKE_VERSION}) + project(reflect VERSION 0.1) enable_language(CXX) From 4430470f568a44d2b1ec325368cb3a637fe0c33c Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 15:08:43 -0400 Subject: [PATCH 022/111] github: debug-find while configuring refcnt (cf for reflect) --- .github/workflows/cmake-single-platform.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index a9b9ac4f..58b3e490 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -64,7 +64,7 @@ jobs: - name: Configure refcnt - run: cmake -B ${{github.workspace}}/build_refcnt -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/refcnt + run: cmake -B ${{github.workspace}}/build_refcnt -DCMAKE_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local --debug-find repo/refcnt - name: Build refcnt run: cmake --build ${{github.workspace}}/build_refcnt --config ${{env.BUILD_TYPE}} From 34b7722410c44568e0420c74af2f8f08b7aa9d1c Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 15:13:03 -0400 Subject: [PATCH 023/111] github: try triggering indentlog failure --- src/reflect/CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/reflect/CMakeLists.txt b/src/reflect/CMakeLists.txt index af38df15..d30bd794 100644 --- a/src/reflect/CMakeLists.txt +++ b/src/reflect/CMakeLists.txt @@ -46,6 +46,15 @@ target_include_directories( ) target_link_libraries(${SELF_LIBRARY_NAME} PUBLIC indentlog) +find_package(refcnt CONFIG REQUIRED) +#add_dependencies(${SELF_LIBRARY_NAME} refcnt) +target_include_directories( + ${SELF_LIBRARY_NAME} PUBLIC + $ + $ +) +target_link_libraries(${SELF_LIBRARY_NAME} PUBLIC refcnt) + # ---------------------------------------------------------------- # 3rd party dependency: boost: From 75cfc477b8cce8553cf04052d23822cc7c8be811 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 15:45:03 -0400 Subject: [PATCH 024/111] github: more experiments w/ --debug-find --- .github/workflows/cmake-single-platform.yml | 6 +++--- src/reflect/CMakeLists.txt | 9 --------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index 58b3e490..2271e790 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -31,7 +31,7 @@ jobs: path: repo/indentlog - name: Configure indentlog - run: cmake -B ${{github.workspace}}/build_indentlog -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/indentlog + run: cmake -B ${{github.workspace}}/build_indentlog -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local --debug-find repo/indentlog - name: Build indentlog run: cmake --build ${{github.workspace}}/build_indentlog --config ${{env.BUILD_TYPE}} @@ -47,7 +47,7 @@ jobs: path: repo/subsys - name: Configure subsys - run: cmake -B ${{github.workspace}}/build_subsys -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/subsys + run: cmake -B ${{github.workspace}}/build_subsys -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local --debug-find repo/subsys - name: Build subsys @@ -56,13 +56,13 @@ jobs: - name: Install subsys run: cmake --install ${{github.workspace}}/build_subsys + - name: Clone refcnt uses: actions/checkout@v3 with: repository: Rconybea/refcnt path: repo/refcnt - - name: Configure refcnt run: cmake -B ${{github.workspace}}/build_refcnt -DCMAKE_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local --debug-find repo/refcnt diff --git a/src/reflect/CMakeLists.txt b/src/reflect/CMakeLists.txt index d30bd794..0590298b 100644 --- a/src/reflect/CMakeLists.txt +++ b/src/reflect/CMakeLists.txt @@ -25,15 +25,6 @@ xo_install_library(${SELF_LIBRARY_NAME}) #xo_refcnt_dependency(${SELF_LIBRARY_NAME}) #xo_indentlog_dependency(${SELF_LIBRARY_NAME}) -find_package(refcnt CONFIG REQUIRED) -#add_dependencies(${SELF_LIBRARY_NAME} refcnt) -target_include_directories( - ${SELF_LIBRARY_NAME} PUBLIC - $ - $ -) -target_link_libraries(${SELF_LIBRARY_NAME} PUBLIC refcnt) - find_package(indentlog CONFIG REQUIRED) #add_dependencies(${SELF_LIBRARY_NAME} indentlog) # note: can't use find_package() here, From 2bd1b19388a6b6c684be8422b8f8311b73387c55 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 15:55:03 -0400 Subject: [PATCH 025/111] github: more weird build experiments --- .github/workflows/cmake-single-platform.yml | 1 - CMakeLists.txt | 3 +++ src/reflect/CMakeLists.txt | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index 2271e790..d31e3138 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -80,7 +80,6 @@ jobs: - name: Configure 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_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} --debug-find - name: Build reflect # Build your program with the given configuration diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ddd1ad2..b9b28560 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,6 +47,9 @@ if(NOT CMAKE_INSTALL_RPATH) set(CMAKE_INSTALL_RPATH $(CMAKE_INSTALL_PREFIX)/lib CACHE STRING "runpath in installed libraries/executables") endif() +# early find_package() experiment +find_package(indentlog CONFIG REQUIRED) + # ---------------------------------------------------------------- # sources diff --git a/src/reflect/CMakeLists.txt b/src/reflect/CMakeLists.txt index 0590298b..dd26ec9c 100644 --- a/src/reflect/CMakeLists.txt +++ b/src/reflect/CMakeLists.txt @@ -25,7 +25,6 @@ xo_install_library(${SELF_LIBRARY_NAME}) #xo_refcnt_dependency(${SELF_LIBRARY_NAME}) #xo_indentlog_dependency(${SELF_LIBRARY_NAME}) -find_package(indentlog CONFIG REQUIRED) #add_dependencies(${SELF_LIBRARY_NAME} indentlog) # note: can't use find_package() here, # because find_package() needs to run successfully before From 4467586ca059b637cd5fb38e6ac4a264efc3fb2a Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 15:57:56 -0400 Subject: [PATCH 026/111] github: big revert -- adopt refcnt .yml, try to build up from there --- .github/workflows/cmake-single-platform.yml | 57 +++++---------------- 1 file changed, 14 insertions(+), 43 deletions(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index d31e3138..b8f26927 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -20,9 +20,11 @@ jobs: runs-on: ubuntu-latest steps: - - name: Install catch2 - run: sudo apt-get install -y catch2 + - 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 indentlog uses: actions/checkout@v3 @@ -30,63 +32,32 @@ jobs: repository: Rconybea/indentlog path: repo/indentlog + + - name: Configure indentlog - run: cmake -B ${{github.workspace}}/build_indentlog -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local --debug-find repo/indentlog + # configure cmake for indentlog in dedicated build directory. + run: cmake -B ${{github.workspace}}/build_indentlog -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 - run: cmake -B ${{github.workspace}}/build_subsys -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local --debug-find repo/subsys - - - - name: Build subsys - run: cmake --build ${{github.workspace}}/build_subsys --config ${{env.BUILD_TYPE}} - - - name: Install subsys - run: cmake --install ${{github.workspace}}/build_subsys - - - - name: Clone refcnt - uses: actions/checkout@v3 - with: - repository: Rconybea/refcnt - path: repo/refcnt - name: Configure refcnt - run: cmake -B ${{github.workspace}}/build_refcnt -DCMAKE_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local --debug-find repo/refcnt - - - name: Build refcnt - run: cmake --build ${{github.workspace}}/build_refcnt --config ${{env.BUILD_TYPE}} - - - name: Install refcnt - run: cmake --install ${{github.workspace}}/build_refcnt - - - name: Checkout reflect - uses: actions/checkout@v3 - - - - - name: Configure 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_refcnt -DCMAKE_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} --debug-find - - name: Build reflect + - name: Build refcnt # Build your program with the given configuration - run: cmake --build ${{github.workspace}}/build_reflect --config ${{env.BUILD_TYPE}} + run: cmake --build ${{github.workspace}}/build_refcnt --config ${{env.BUILD_TYPE}} - - name: Test reflect - working-directory: ${{github.workspace}}/build_reflect + - name: Test refcnt + working-directory: ${{github.workspace}}/build_refcnt # 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}} From 40a894425852b89a4d0620a5d8b513d60f36e4ca Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 16:01:32 -0400 Subject: [PATCH 027/111] github: experiment, take 2 --- .github/workflows/cmake-single-platform.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index b8f26927..a57b11a5 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -26,14 +26,13 @@ jobs: # 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 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_INSTALL_PREFIX=${{github.workspace}}/local repo/indentlog @@ -46,18 +45,17 @@ jobs: run: cmake --install ${{github.workspace}}/build_indentlog - - - name: Configure refcnt + - name: Configure 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_refcnt -DCMAKE_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} --debug-find + run: cmake -B ${{github.workspace}}/build_reflect -DCMAKE_PREFIX_PATH=${{github.workspace}}/xyz -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} --debug-find - - name: Build refcnt + - name: Build reflect # Build your program with the given configuration - run: cmake --build ${{github.workspace}}/build_refcnt --config ${{env.BUILD_TYPE}} + run: cmake --build ${{github.workspace}}/build_reflect --config ${{env.BUILD_TYPE}} - - name: Test refcnt - working-directory: ${{github.workspace}}/build_refcnt + - name: Test 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}} From 7a40acc8b53d8c5d686e0bb049b28c9cb0ecece4 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 16:08:21 -0400 Subject: [PATCH 028/111] github: experiment 3 --- .github/workflows/cmake-single-platform.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index a57b11a5..7d660c32 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -45,10 +45,10 @@ jobs: run: cmake --install ${{github.workspace}}/build_indentlog - - name: Configure reflect + - name: Configure reflect library # 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_PREFIX_PATH=${{github.workspace}}/xyz -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} --debug-find + run: cmake -B ${{github.workspace}}/build_reflect -DCMAKE_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} --debug-find - name: Build reflect # Build your program with the given configuration From 031d0bf887edbb6ad46832f8d3b74c35514ab172 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 16:13:10 -0400 Subject: [PATCH 029/111] github: experiment 4 --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index b9b28560..02c30bf3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,7 @@ endif() # early find_package() experiment find_package(indentlog CONFIG REQUIRED) +find_package(refcnt2 CONFIG REQUIRED) # ---------------------------------------------------------------- # sources From 0c7f78f721ed0f7afea340b4d1947351f5573ab2 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 16:14:36 -0400 Subject: [PATCH 030/111] github: experiment 5 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 02c30bf3..cdd7648d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,7 +49,7 @@ endif() # early find_package() experiment find_package(indentlog CONFIG REQUIRED) -find_package(refcnt2 CONFIG REQUIRED) +find_package(refcnt CONFIG REQUIRED) # ---------------------------------------------------------------- # sources From 6e43bb55f04649faea813dff48fe48c443f3c2d3 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 16:16:38 -0400 Subject: [PATCH 031/111] github: fetch+install refcnt again --- .github/workflows/cmake-single-platform.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index 7d660c32..95d1b2b5 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -45,6 +45,24 @@ jobs: run: cmake --install ${{github.workspace}}/build_indentlog + - 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_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 reflect library # 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 From 63d5267913b3157ba756c5ddd284c84ca325f5e4 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 16:18:54 -0400 Subject: [PATCH 032/111] github: fetch+install subsys again --- .github/workflows/cmake-single-platform.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index 95d1b2b5..55c366a2 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -45,6 +45,24 @@ jobs: 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_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: From efb1b9aa473fd87e4f9211549bc62000016da1ec Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 16:23:46 -0400 Subject: [PATCH 033/111] github: try restoring refcnt CMAKE_PREFIX_PATH argument --- .github/workflows/cmake-single-platform.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index 55c366a2..24e4b579 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -71,7 +71,7 @@ jobs: - name: Configure refcnt # configure cmake for refcnt in dedicated build directory. - run: cmake -B ${{github.workspace}}/build_refcnt -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/refcnt + run: cmake -B ${{github.workspace}}/build_refcnt -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}} From ebc70db3e734c8abec8344c2c0c5fe282ae9d037 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 27 Sep 2023 09:39:54 -0400 Subject: [PATCH 034/111] build: adopt xo-cmake dependency --- .github/workflows/cmake-single-platform.yml | 35 ++++++++++++++++----- CMakeLists.txt | 2 ++ README.md | 4 +-- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index 24e4b579..a8ce3a72 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -26,6 +26,24 @@ jobs: # 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 @@ -35,7 +53,7 @@ jobs: - name: Configure indentlog # configure cmake for indentlog in dedicated build directory. - run: cmake -B ${{github.workspace}}/build_indentlog -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/indentlog + 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}} @@ -44,6 +62,7 @@ jobs: # install into ${{github.workspace}}/local run: cmake --install ${{github.workspace}}/build_indentlog + # ---------------------------------------------------------------- - name: Clone subsys uses: actions/checkout@v3 @@ -53,7 +72,7 @@ jobs: - name: Configure subsys # configure cmake for subsys in dedicated build directory. - run: cmake -B ${{github.workspace}}/build_subsys -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/subsys + 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}} @@ -62,6 +81,7 @@ jobs: # install into ${{github.workspace}}/local run: cmake --install ${{github.workspace}}/build_subsys + # ---------------------------------------------------------------- - name: Clone refcnt uses: actions/checkout@v3 @@ -71,7 +91,7 @@ jobs: - name: Configure refcnt # configure cmake for refcnt in dedicated build directory. - run: cmake -B ${{github.workspace}}/build_refcnt -DCMAKE_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/refcnt + 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}} @@ -80,17 +100,18 @@ jobs: # install into ${{github.workspace}}/local run: cmake --install ${{github.workspace}}/build_refcnt + # ---------------------------------------------------------------- - - name: Configure reflect library + - 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_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} --debug-find + 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 reflect + - name: Build self (reflect) # Build your program with the given configuration run: cmake --build ${{github.workspace}}/build_reflect --config ${{env.BUILD_TYPE}} - - name: Test reflect + - 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 diff --git a/CMakeLists.txt b/CMakeLists.txt index cdd7648d..afa760ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,8 @@ message(CMAKE_VERSION=${CMAKE_VERSION}) project(reflect VERSION 0.1) enable_language(CXX) +# common XO cmake macros (see proj/xo-cmake) +include(xo_macros/xo_cxx) include(cmake/cxx.cmake) include(cmake/code-coverage.cmake) diff --git a/README.md b/README.md index fb6682d4..0cd50f02 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ $ cd reflect $ mkdir build $ cd build -$ cmake -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} .. +$ cmake -DCMAKE_MODULE_PATH=${INSTALL_PREFIX}/share/cmake -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} .. $ make $ make install ``` @@ -15,5 +15,5 @@ $ make install $ cd refcnt $ mkdir build-ccov $ cd build-ccov -$ cmake -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} -DCODE_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug .. +$ cmake -DCMAKE_MODULE_PATH=${INSTALL_PREFIX}/share/cmake -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} -DCODE_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug .. ``` From eafb897dfd0986f4643c838d9f644410ff00572b Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 27 Sep 2023 13:22:02 -0400 Subject: [PATCH 035/111] reflect: build: use new xo-cmake macros --- CMakeLists.txt | 92 ++++++++++++++++++++------------------ src/reflect/CMakeLists.txt | 34 +++++++------- 2 files changed, 68 insertions(+), 58 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index afa760ec..d2718ef2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,52 +60,58 @@ add_subdirectory(src/reflect) add_subdirectory(utest) # ---------------------------------------------------------------- -# cmake export: +# provide find_package() support + +xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) + +## ---------------------------------------------------------------- +## cmake export: +## +## populate .cmake files in $CMAKE_INSTALL_LIBDIR/cmake/reflect. +## cmake projects that include this directory in $CMAKE_PREFIX_PATH +## can use +## find_package(reflect REQUIRED) +## and +## target_link_libraries(${sometarget} PUBLIC reflect) +## to use the reflect library # -# populate .cmake files in $CMAKE_INSTALL_LIBDIR/cmake/reflect. -# cmake projects that include this directory in $CMAKE_PREFIX_PATH -# can use -# find_package(reflect REQUIRED) -# and -# target_link_libraries(${sometarget} PUBLIC reflect) -# to use the reflect library - -set(XO_PROJECT_CONFIG_VERSION "${XO_PROJECT_NAME}ConfigVersion.cmake") -set(XO_PROJECT_CONFIG "${XO_PROJECT_NAME}Config.cmake") - -include(CMakePackageConfigHelpers) - -# generates build/reflectConfigVersion.cmake -write_basic_package_version_file( - "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG_VERSION}" - VERSION 0.1 - COMPATIBILITY AnyNewerVersion -) - -# generates build/reflectConfig.cmake -configure_package_config_file( - "${PROJECT_SOURCE_DIR}/cmake/${XO_PROJECT_NAME}Config.cmake.in" - "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG}" - INSTALL_DESTINATION lib/cmake/${XO_PROJECT_NAME} -) - -# creates {reflectTargets.cmake, reflectTargets-noconfig.cmake} in $CMAKE_INSTALL_LIBDIR/cmake/reflect/ -# requires -# install(.. EXPORT reflectTargets ..) +#set(XO_PROJECT_CONFIG_VERSION "${XO_PROJECT_NAME}ConfigVersion.cmake") +#set(XO_PROJECT_CONFIG "${XO_PROJECT_NAME}Config.cmake") # -install( - EXPORT ${XO_PROJECT_NAME}Targets - DESTINATION lib/cmake/${XO_PROJECT_NAME} -) - -# creates {reflectConfigVersion.cmake, reflectConfig.cmake} in $CMAKE_INSTALL_LIBDIR/cmake/reflect/ -install( - FILES - "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG_VERSION}" - "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG}" - DESTINATION lib/cmake/${XO_PROJECT_NAME}) +#include(CMakePackageConfigHelpers) +# +## generates build/reflectConfigVersion.cmake +#write_basic_package_version_file( +# "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG_VERSION}" +# VERSION 0.1 +# COMPATIBILITY AnyNewerVersion +#) +# +## generates build/reflectConfig.cmake +#configure_package_config_file( +# "${PROJECT_SOURCE_DIR}/cmake/${XO_PROJECT_NAME}Config.cmake.in" +# "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG}" +# INSTALL_DESTINATION lib/cmake/${XO_PROJECT_NAME} +#) +# +## creates {reflectTargets.cmake, reflectTargets-noconfig.cmake} in $CMAKE_INSTALL_LIBDIR/cmake/reflect/ +## requires +## install(.. EXPORT reflectTargets ..) +## +#install( +# EXPORT ${XO_PROJECT_NAME}Targets +# DESTINATION lib/cmake/${XO_PROJECT_NAME} +#) +# +## creates {reflectConfigVersion.cmake, reflectConfig.cmake} in $CMAKE_INSTALL_LIBDIR/cmake/reflect/ +#install( +# FILES +# "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG_VERSION}" +# "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG}" +# DESTINATION lib/cmake/${XO_PROJECT_NAME}) # ---------------------------------------------------------------- # install .hpp files -install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/reflect/ DESTINATION include/reflect) +xo_install_include_tree() +#install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/reflect/ DESTINATION include/reflect) diff --git a/src/reflect/CMakeLists.txt b/src/reflect/CMakeLists.txt index dd26ec9c..d4b772e4 100644 --- a/src/reflect/CMakeLists.txt +++ b/src/reflect/CMakeLists.txt @@ -16,12 +16,18 @@ set_target_properties(${SELF_LIBRARY_NAME} # #target_compile_options(${SELF_LIBRARY_NAME} PRIVATE -Werror -Wall -Wextra) xo_compile_options(${SELF_LIBRARY_NAME}) -xo_include_options(${SELF_LIBRARY_NAME}) +xo_include_options2(${SELF_LIBRARY_NAME}) xo_install_library(${SELF_LIBRARY_NAME}) # ---------------------------------------------------------------- # dependencies: logutil, ... +#target_include_directories( +# ${SELF_LIBRARY_NAME} PUBLIC +# $ +# $ +#) + #xo_refcnt_dependency(${SELF_LIBRARY_NAME}) #xo_indentlog_dependency(${SELF_LIBRARY_NAME}) @@ -29,21 +35,19 @@ xo_install_library(${SELF_LIBRARY_NAME}) # note: can't use find_package() here, # because find_package() needs to run successfully before # dependency gets installed. -target_include_directories( - ${SELF_LIBRARY_NAME} PUBLIC - $ - $ -) -target_link_libraries(${SELF_LIBRARY_NAME} PUBLIC indentlog) +xo_internal_dependency(${SELF_LIBRARY_NAME} indentlog) -find_package(refcnt CONFIG REQUIRED) -#add_dependencies(${SELF_LIBRARY_NAME} refcnt) -target_include_directories( - ${SELF_LIBRARY_NAME} PUBLIC - $ - $ -) -target_link_libraries(${SELF_LIBRARY_NAME} PUBLIC refcnt) +#target_link_libraries(${SELF_LIBRARY_NAME} PUBLIC indentlog) + +xo_internal_dependency(${SELF_LIBRARY_NAME} refcnt) +#find_package(refcnt CONFIG REQUIRED) +##add_dependencies(${SELF_LIBRARY_NAME} refcnt) +#target_include_directories( +# ${SELF_LIBRARY_NAME} PUBLIC +# $ +# $ +#) +#target_link_libraries(${SELF_LIBRARY_NAME} PUBLIC refcnt) # ---------------------------------------------------------------- # 3rd party dependency: boost: From 79bb624ec8bf5199b281bfc48d77aaf27b9646fa Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 27 Sep 2023 14:03:55 -0400 Subject: [PATCH 036/111] reflect: build: retire superseded cmake macros --- cmake/cxx.cmake | 113 +++++++++++++++++++------------------ src/reflect/CMakeLists.txt | 2 +- utest/CMakeLists.txt | 2 +- 3 files changed, 59 insertions(+), 58 deletions(-) diff --git a/cmake/cxx.cmake b/cmake/cxx.cmake index fa6efb89..4084bac9 100644 --- a/cmake/cxx.cmake +++ b/cmake/cxx.cmake @@ -1,36 +1,36 @@ -# ---------------------------------------------------------------- -# use this in subdirs that compile c++ code +## ---------------------------------------------------------------- +## use this in subdirs that compile c++ code +## +#macro(xo_include_options target) +# # ---------------------------------------------------------------- +# # PROJECT_SOURCE_DIR: +# # so we can for example write +# # #include "ordinaltree/foo.hpp" +# # from anywhere in the project +# # PROJECT_BINARY_DIR: +# # since generated version file will be in build directory, +# # need that build directory to also appear in +# # compiler's include path +# # +# target_include_directories( +# ${target} PUBLIC +# $ # e.g. for #include "indentlog/scope.hpp" +# $ +# $ # e.g. for #include "Refcounted.hpp" in refcnt/src +# $ +# $ # e.g. for generated config.hpp file +# ) # -macro(xo_include_options target) - # ---------------------------------------------------------------- - # PROJECT_SOURCE_DIR: - # so we can for example write - # #include "ordinaltree/foo.hpp" - # from anywhere in the project - # PROJECT_BINARY_DIR: - # since generated version file will be in build directory, - # need that build directory to also appear in - # compiler's include path - # - target_include_directories( - ${target} PUBLIC - $ # e.g. for #include "indentlog/scope.hpp" - $ - $ # e.g. for #include "Refcounted.hpp" in refcnt/src - $ - $ # e.g. for generated config.hpp file - ) - - # ---------------------------------------------------------------- - # make standard directories for std:: includes explicit - # so that - # (1) they appear in compile_commands.json. - # (2) clangd (run from emacs lsp-mode) can find them - # - if(CMAKE_EXPORT_COMPILE_COMMANDS) - set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) - endif() -endmacro() +# # ---------------------------------------------------------------- +# # make standard directories for std:: includes explicit +# # so that +# # (1) they appear in compile_commands.json. +# # (2) clangd (run from emacs lsp-mode) can find them +# # +# if(CMAKE_EXPORT_COMPILE_COMMANDS) +# set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) +# endif() +#endmacro() # ---------------------------------------------------------------- # variable @@ -70,29 +70,30 @@ macro(xo_compile_options target) target_compile_options(${target} PRIVATE ${XO_COMPILE_OPTIONS}) endmacro() -# ---------------------------------------------------------------- -# use this for a subdir that builds a library -# EXPORT drives .cmake config files intended for consumption -# by higher-level cmake projects via find_package() +## ---------------------------------------------------------------- +## use this for a subdir that builds a library +## EXPORT drives .cmake config files intended for consumption +## by higher-level cmake projects via find_package() +## +#macro(xo_install_library target) +# install( +# TARGETS ${target} +# EXPORT ${target}Targets +# LIBRARY DESTINATION lib COMPONENT Runtime +# ARCHIVE DESTINATION lib COMPONENT Development +# RUNTIME DESTINATION bin COMPONENT Runtime +# PUBLIC_HEADER DESTINATION include COMPONENT Development +# BUNDLE DESTINATION bin COMPONENT Runtime +# ) +#endmacro() # -macro(xo_install_library target) - install( - TARGETS ${target} - EXPORT ${target}Targets - LIBRARY DESTINATION lib COMPONENT Runtime - ARCHIVE DESTINATION lib COMPONENT Development - RUNTIME DESTINATION bin COMPONENT Runtime - PUBLIC_HEADER DESTINATION include COMPONENT Development - BUNDLE DESTINATION bin COMPONENT Runtime - ) -endmacro() - -# ---------------------------------------------------------------- -# use this when relying on indentlog [[https://github.com/rconybea/indentlog]] headers +## ---------------------------------------------------------------- +## use this when relying on indentlog [[https://github.com/rconybea/indentlog]] headers +## +#macro(xo_indentlog_dependency target) +# find_package(indentlog REQUIRED) +# #add_dependencies(${target} indentlog) +# target_link_libraries(${target} PUBLIC indentlog) +# #target_include_directories(${target} PUBLIC ${indentlog_DIR}/../../../include) +#endmacro() # -macro(xo_indentlog_dependency target) - find_package(indentlog REQUIRED) - #add_dependencies(${target} indentlog) - target_link_libraries(${target} PUBLIC indentlog) - #target_include_directories(${target} PUBLIC ${indentlog_DIR}/../../../include) -endmacro() diff --git a/src/reflect/CMakeLists.txt b/src/reflect/CMakeLists.txt index d4b772e4..bf709478 100644 --- a/src/reflect/CMakeLists.txt +++ b/src/reflect/CMakeLists.txt @@ -17,7 +17,7 @@ set_target_properties(${SELF_LIBRARY_NAME} #target_compile_options(${SELF_LIBRARY_NAME} PRIVATE -Werror -Wall -Wextra) xo_compile_options(${SELF_LIBRARY_NAME}) xo_include_options2(${SELF_LIBRARY_NAME}) -xo_install_library(${SELF_LIBRARY_NAME}) +xo_install_library2(${SELF_LIBRARY_NAME}) # ---------------------------------------------------------------- # dependencies: logutil, ... diff --git a/utest/CMakeLists.txt b/utest/CMakeLists.txt index 80ce49ac..7eae2825 100644 --- a/utest/CMakeLists.txt +++ b/utest/CMakeLists.txt @@ -4,7 +4,7 @@ set(SELF_EXECUTABLE_NAME utest.reflect) set(SELF_SOURCE_FILES reflect_utest_main.cpp StructReflector.test.cpp VectorTdx.test.cpp StructTdx.test.cpp) add_executable(${SELF_EXECUTABLE_NAME} ${SELF_SOURCE_FILES}) -xo_include_options(${SELF_EXECUTABLE_NAME}) +xo_include_options2(${SELF_EXECUTABLE_NAME}) add_test(NAME ${SELF_EXECUTABLE_NAME} COMMAND ${SELF_EXECUTABLE_NAME}) target_code_coverage(${SELF_EXECUTABLE_NAME} AUTO ALL) From 7a58d584700d106de9d1efad40e7000f20537a44 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 27 Sep 2023 16:08:43 -0400 Subject: [PATCH 037/111] reflect: build: use xo_toplevel_compile_options() to simplify --- CMakeLists.txt | 3 ++ cmake/cxx.cmake | 122 ++++++++++++------------------------------------ 2 files changed, 33 insertions(+), 92 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d2718ef2..7b0240d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,10 +45,13 @@ set(CMAKE_CXX_STANDARD_REQUIRED True) # always write compile_commands.json set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "") +xo_toplevel_compile_options() + if(NOT CMAKE_INSTALL_RPATH) set(CMAKE_INSTALL_RPATH $(CMAKE_INSTALL_PREFIX)/lib CACHE STRING "runpath in installed libraries/executables") endif() + # early find_package() experiment find_package(indentlog CONFIG REQUIRED) find_package(refcnt CONFIG REQUIRED) diff --git a/cmake/cxx.cmake b/cmake/cxx.cmake index 4084bac9..ede44e35 100644 --- a/cmake/cxx.cmake +++ b/cmake/cxx.cmake @@ -1,99 +1,37 @@ ## ---------------------------------------------------------------- -## use this in subdirs that compile c++ code -## -#macro(xo_include_options target) -# # ---------------------------------------------------------------- -# # PROJECT_SOURCE_DIR: -# # so we can for example write -# # #include "ordinaltree/foo.hpp" -# # from anywhere in the project -# # PROJECT_BINARY_DIR: -# # since generated version file will be in build directory, -# # need that build directory to also appear in -# # compiler's include path -# # -# target_include_directories( -# ${target} PUBLIC -# $ # e.g. for #include "indentlog/scope.hpp" -# $ -# $ # e.g. for #include "Refcounted.hpp" in refcnt/src -# $ -# $ # e.g. for generated config.hpp file -# ) -# -# # ---------------------------------------------------------------- -# # make standard directories for std:: includes explicit -# # so that -# # (1) they appear in compile_commands.json. -# # (2) clangd (run from emacs lsp-mode) can find them -# # -# if(CMAKE_EXPORT_COMPILE_COMMANDS) -# set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) -# endif() -#endmacro() - -# ---------------------------------------------------------------- -# variable -# XO_ADDRESS_SANITIZE -# determines whether to enable address sanitizer for the XO project -# (see toplevel CMakeLists.txt) -# ---------------------------------------------------------------- -if(XO_ADDRESS_SANITIZE) - add_compile_options(-fsanitize=address) - add_link_options(-fsanitize=address) -endif() - -# XO_STANDARD_COMPILE_OPTIONS: use these when XO_ADDRESS_SANITIZE=OFF -set(XO_STANDARD_COMPILE_OPTIONS -Werror -Wall -Wextra) - -# XO_ADDRESS_SANITIZE_COMPILE_OPTIONS: use when XO_ADDRESS_SANITIZE=ON -# -# address sanitizer build complains about _FORTIFY_SOURCE redefines -# In file included from :460: -# :1:9: error: '_FORTIFY_SOURCE' macro redefined [-Werror,-Wmacro-redefined] -# #define _FORTIFY_SOURCE 2 -# -set(XO_ADDRESS_SANITIZE_COMPILE_OPTIONS -Werror -Wall -Wextra -Wno-macro-redefined) - -# XO_COMPILE_OPTIONS: use these with xo_compile_options() macro -if(XO_ADDRESS_SANITIZE) - set(XO_COMPILE_OPTIONS ${XO_ADDRESS_SANITIZE_COMPILE_OPTIONS}) -else() - set(XO_COMPILE_OPTIONS ${XO_STANDARD_COMPILE_OPTIONS}) -endif() - -# ---------------------------------------------------------------- -# generally want all the errors+warnings! -# however: address sanitizer generates error on _FORTIFY_SOURCE -# -macro(xo_compile_options target) - target_compile_options(${target} PRIVATE ${XO_COMPILE_OPTIONS}) -endmacro() - +## variable +## XO_ADDRESS_SANITIZE +## determines whether to enable address sanitizer for the XO project +## (see toplevel CMakeLists.txt) ## ---------------------------------------------------------------- -## use this for a subdir that builds a library -## EXPORT drives .cmake config files intended for consumption -## by higher-level cmake projects via find_package() +#if(XO_ADDRESS_SANITIZE) +# add_compile_options(-fsanitize=address) +# add_link_options(-fsanitize=address) +#endif() +# +## XO_STANDARD_COMPILE_OPTIONS: use these when XO_ADDRESS_SANITIZE=OFF +#set(XO_STANDARD_COMPILE_OPTIONS -Werror -Wall -Wextra) +# +## XO_ADDRESS_SANITIZE_COMPILE_OPTIONS: use when XO_ADDRESS_SANITIZE=ON ## -#macro(xo_install_library target) -# install( -# TARGETS ${target} -# EXPORT ${target}Targets -# LIBRARY DESTINATION lib COMPONENT Runtime -# ARCHIVE DESTINATION lib COMPONENT Development -# RUNTIME DESTINATION bin COMPONENT Runtime -# PUBLIC_HEADER DESTINATION include COMPONENT Development -# BUNDLE DESTINATION bin COMPONENT Runtime -# ) -#endmacro() +## address sanitizer build complains about _FORTIFY_SOURCE redefines +## In file included from :460: +## :1:9: error: '_FORTIFY_SOURCE' macro redefined [-Werror,-Wmacro-redefined] +## #define _FORTIFY_SOURCE 2 +## +#set(XO_ADDRESS_SANITIZE_COMPILE_OPTIONS -Werror -Wall -Wextra -Wno-macro-redefined) +# +## XO_COMPILE_OPTIONS: use these with xo_compile_options() macro +#if(XO_ADDRESS_SANITIZE) +# set(XO_COMPILE_OPTIONS ${XO_ADDRESS_SANITIZE_COMPILE_OPTIONS}) +#else() +# set(XO_COMPILE_OPTIONS ${XO_STANDARD_COMPILE_OPTIONS}) +#endif() # ## ---------------------------------------------------------------- -## use this when relying on indentlog [[https://github.com/rconybea/indentlog]] headers +## generally want all the errors+warnings! +## however: address sanitizer generates error on _FORTIFY_SOURCE ## -#macro(xo_indentlog_dependency target) -# find_package(indentlog REQUIRED) -# #add_dependencies(${target} indentlog) -# target_link_libraries(${target} PUBLIC indentlog) -# #target_include_directories(${target} PUBLIC ${indentlog_DIR}/../../../include) +#macro(xo_compile_options target) +# target_compile_options(${target} PRIVATE ${XO_COMPILE_OPTIONS}) #endmacro() -# From ef0b02f4cae31cd7f3e24f2afd6c126fca62e6d1 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 27 Sep 2023 16:32:29 -0400 Subject: [PATCH 038/111] reflect: use xo_macros code coverage --- CMakeLists.txt | 3 +-- cmake/cxx.cmake | 37 ------------------------------------- 2 files changed, 1 insertion(+), 39 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b0240d0..4ca908c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,8 +9,7 @@ enable_language(CXX) # common XO cmake macros (see proj/xo-cmake) include(xo_macros/xo_cxx) -include(cmake/cxx.cmake) -include(cmake/code-coverage.cmake) +include(xo_macros/code-coverage) # ---------------------------------------------------------------- # unit test setup diff --git a/cmake/cxx.cmake b/cmake/cxx.cmake index ede44e35..e69de29b 100644 --- a/cmake/cxx.cmake +++ b/cmake/cxx.cmake @@ -1,37 +0,0 @@ -## ---------------------------------------------------------------- -## variable -## XO_ADDRESS_SANITIZE -## determines whether to enable address sanitizer for the XO project -## (see toplevel CMakeLists.txt) -## ---------------------------------------------------------------- -#if(XO_ADDRESS_SANITIZE) -# add_compile_options(-fsanitize=address) -# add_link_options(-fsanitize=address) -#endif() -# -## XO_STANDARD_COMPILE_OPTIONS: use these when XO_ADDRESS_SANITIZE=OFF -#set(XO_STANDARD_COMPILE_OPTIONS -Werror -Wall -Wextra) -# -## XO_ADDRESS_SANITIZE_COMPILE_OPTIONS: use when XO_ADDRESS_SANITIZE=ON -## -## address sanitizer build complains about _FORTIFY_SOURCE redefines -## In file included from :460: -## :1:9: error: '_FORTIFY_SOURCE' macro redefined [-Werror,-Wmacro-redefined] -## #define _FORTIFY_SOURCE 2 -## -#set(XO_ADDRESS_SANITIZE_COMPILE_OPTIONS -Werror -Wall -Wextra -Wno-macro-redefined) -# -## XO_COMPILE_OPTIONS: use these with xo_compile_options() macro -#if(XO_ADDRESS_SANITIZE) -# set(XO_COMPILE_OPTIONS ${XO_ADDRESS_SANITIZE_COMPILE_OPTIONS}) -#else() -# set(XO_COMPILE_OPTIONS ${XO_STANDARD_COMPILE_OPTIONS}) -#endif() -# -## ---------------------------------------------------------------- -## generally want all the errors+warnings! -## however: address sanitizer generates error on _FORTIFY_SOURCE -## -#macro(xo_compile_options target) -# target_compile_options(${target} PRIVATE ${XO_COMPILE_OPTIONS}) -#endmacro() From e8a79ebb57967e8db79c2a0052018005e0ca45b2 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 27 Sep 2023 16:33:05 -0400 Subject: [PATCH 039/111] reflect: retire superseded .cmake files --- cmake/code-coverage.cmake | 678 -------------------------------------- cmake/cxx.cmake | 0 2 files changed, 678 deletions(-) delete mode 100644 cmake/code-coverage.cmake delete mode 100644 cmake/cxx.cmake diff --git a/cmake/code-coverage.cmake b/cmake/code-coverage.cmake deleted file mode 100644 index b6b36064..00000000 --- a/cmake/code-coverage.cmake +++ /dev/null @@ -1,678 +0,0 @@ -# -# Copyright (C) 2018-2020 by George Cave - gcave@stablecoder.ca -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy of -# the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations under -# the License. - -# USAGE: To enable any code coverage instrumentation/targets, the single CMake -# option of `CODE_COVERAGE` needs to be set to 'ON', either by GUI, ccmake, or -# on the command line. -# -# From this point, there are two primary methods for adding instrumentation to -# targets: -# -# 1 - A blanket instrumentation by calling `add_code_coverage()`, where -# all targets in that directory and all subdirectories are automatically -# instrumented. -# -# 2 - Per-target instrumentation by calling -# `target_code_coverage()`, where the target is given and thus only -# that target is instrumented. This applies to both libraries and executables. -# -# To add coverage targets, such as calling `make ccov` to generate the actual -# coverage information for perusal or consumption, call -# `target_code_coverage()` on an *executable* target. -# -# Example 1: All targets instrumented -# -# In this case, the coverage information reported will will be that of the -# `theLib` library target and `theExe` executable. -# -# 1a: Via global command -# -# ~~~ -# add_code_coverage() # Adds instrumentation to all targets -# -# add_library(theLib lib.cpp) -# -# add_executable(theExe main.cpp) -# target_link_libraries(theExe PRIVATE theLib) -# target_code_coverage(theExe) # As an executable target, adds the 'ccov-theExe' target -# # (instrumentation already added via global anyways) -# # for generating code coverage reports. -# ~~~ -# -# 1b: Via target commands -# -# ~~~ -# add_library(theLib lib.cpp) -# target_code_coverage(theLib) # As a library target, adds coverage instrumentation but no targets. -# -# add_executable(theExe main.cpp) -# target_link_libraries(theExe PRIVATE theLib) -# target_code_coverage(theExe) # As an executable target, adds the 'ccov-theExe' target and instrumentation for generating code coverage reports. -# ~~~ -# -# Example 2: Target instrumented, but with regex pattern of files to be excluded -# from report -# -# ~~~ -# add_executable(theExe main.cpp non_covered.cpp) -# target_code_coverage(theExe EXCLUDE non_covered.cpp test/*) # As an executable target, the reports will exclude the non-covered.cpp file, and any files in a test/ folder. -# ~~~ -# -# Example 3: Target added to the 'ccov' and 'ccov-all' targets -# -# ~~~ -# add_code_coverage_all_targets(EXCLUDE test/*) # Adds the 'ccov-all' target set and sets it to exclude all files in test/ folders. -# -# add_executable(theExe main.cpp non_covered.cpp) -# target_code_coverage(theExe AUTO ALL EXCLUDE non_covered.cpp test/*) # As an executable target, adds to the 'ccov' and ccov-all' targets, and the reports will exclude the non-covered.cpp file, and any files in a test/ folder. -# ~~~ - -# Options -option( - CODE_COVERAGE - "Builds targets with code coverage instrumentation. (Requires GCC or Clang)" - OFF) - -# Programs -find_program(LLVM_COV_PATH llvm-cov) -find_program(LLVM_PROFDATA_PATH llvm-profdata) -find_program(LCOV_PATH lcov) -find_program(GENHTML_PATH genhtml) -# Hide behind the 'advanced' mode flag for GUI/ccmake -mark_as_advanced(FORCE LLVM_COV_PATH LLVM_PROFDATA_PATH LCOV_PATH GENHTML_PATH) - -# Variables -set(CMAKE_COVERAGE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/ccov) -set_property(GLOBAL PROPERTY JOB_POOLS ccov_serial_pool=1) - -# Common initialization/checks -if(CODE_COVERAGE AND NOT CODE_COVERAGE_ADDED) - set(CODE_COVERAGE_ADDED ON) - - # Common Targets - add_custom_target( - ccov-preprocessing - COMMAND ${CMAKE_COMMAND} -E make_directory - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY} - DEPENDS ccov-clean) - - if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" - OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") - # Messages - message(STATUS "Building with llvm Code Coverage Tools") - - if(NOT LLVM_COV_PATH) - message(FATAL_ERROR "llvm-cov not found! Aborting.") - else() - # Version number checking for 'EXCLUDE' compatibility - execute_process(COMMAND ${LLVM_COV_PATH} --version - OUTPUT_VARIABLE LLVM_COV_VERSION_CALL_OUTPUT) - string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" LLVM_COV_VERSION - ${LLVM_COV_VERSION_CALL_OUTPUT}) - - if(LLVM_COV_VERSION VERSION_LESS "7.0.0") - message( - WARNING - "target_code_coverage()/add_code_coverage_all_targets() 'EXCLUDE' option only available on llvm-cov >= 7.0.0" - ) - endif() - endif() - - # Targets - if(${CMAKE_VERSION} VERSION_LESS "3.17.0") - add_custom_target( - ccov-clean - COMMAND ${CMAKE_COMMAND} -E remove -f - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list - COMMAND ${CMAKE_COMMAND} -E remove -f - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list) - else() - add_custom_target( - ccov-clean - COMMAND ${CMAKE_COMMAND} -E rm -f - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list - COMMAND ${CMAKE_COMMAND} -E rm -f - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list) - endif() - - # Used to get the shared object file list before doing the main all- - # processing - add_custom_target( - ccov-libs - COMMAND ; - COMMENT "libs ready for coverage report.") - - elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES - "GNU") - # Messages - message(STATUS "Building with lcov Code Coverage Tools") - - if(CMAKE_BUILD_TYPE) - string(TOUPPER ${CMAKE_BUILD_TYPE} upper_build_type) - if(NOT ${upper_build_type} STREQUAL "DEBUG") - message( - WARNING - "Code coverage results with an optimized (non-Debug) build may be misleading" - ) - endif() - else() - message( - WARNING - "Code coverage results with an optimized (non-Debug) build may be misleading" - ) - endif() - if(NOT LCOV_PATH) - message(FATAL_ERROR "lcov not found! Aborting...") - endif() - if(NOT GENHTML_PATH) - message(FATAL_ERROR "genhtml not found! Aborting...") - endif() - - # Targets - add_custom_target(ccov-clean COMMAND ${LCOV_PATH} --directory - ${CMAKE_BINARY_DIR} --zerocounters) - - else() - message(FATAL_ERROR "Code coverage requires Clang or GCC. Aborting.") - endif() -endif() - -# Adds code coverage instrumentation to a library, or instrumentation/targets -# for an executable target. -# ~~~ -# EXECUTABLE ADDED TARGETS: -# GCOV/LCOV: -# ccov : Generates HTML code coverage report for every target added with 'AUTO' parameter. -# ccov-${TARGET_NAME} : Generates HTML code coverage report for the associated named target. -# ccov-all : Generates HTML code coverage report, merging every target added with 'ALL' parameter into a single detailed report. -# -# LLVM-COV: -# ccov : Generates HTML code coverage report for every target added with 'AUTO' parameter. -# ccov-report : Generates HTML code coverage report for every target added with 'AUTO' parameter. -# ccov-${TARGET_NAME} : Generates HTML code coverage report. -# ccov-report-${TARGET_NAME} : Prints to command line summary per-file coverage information. -# ccov-export-${TARGET_NAME} : Exports the coverage report to a JSON file. -# ccov-show-${TARGET_NAME} : Prints to command line detailed per-line coverage information. -# ccov-all : Generates HTML code coverage report, merging every target added with 'ALL' parameter into a single detailed report. -# ccov-all-report : Prints summary per-file coverage information for every target added with ALL' parameter to the command line. -# ccov-all-export : Exports the coverage report to a JSON file. -# -# Required: -# TARGET_NAME - Name of the target to generate code coverage for. -# Optional: -# PUBLIC - Sets the visibility for added compile options to targets to PUBLIC instead of the default of PRIVATE. -# INTERFACE - Sets the visibility for added compile options to targets to INTERFACE instead of the default of PRIVATE. -# PLAIN - Do not set any target visibility (backward compatibility with old cmake projects) -# AUTO - Adds the target to the 'ccov' target so that it can be run in a batch with others easily. Effective on executable targets. -# ALL - Adds the target to the 'ccov-all' and 'ccov-all-report' targets, which merge several executable targets coverage data to a single report. Effective on executable targets. -# EXTERNAL - For GCC's lcov, allows the profiling of 'external' files from the processing directory -# COVERAGE_TARGET_NAME - For executables ONLY, changes the outgoing target name so instead of `ccov-${TARGET_NAME}` it becomes `ccov-${COVERAGE_TARGET_NAME}`. -# EXCLUDE - Excludes files of the patterns provided from coverage. Note that GCC/lcov excludes by glob pattern, and clang/LLVM excludes via regex! **These do not copy to the 'all' targets.** -# OBJECTS - For executables ONLY, if the provided targets are shared libraries, adds coverage information to the output -# ARGS - For executables ONLY, appends the given arguments to the associated ccov-* executable call -# ~~~ -function(target_code_coverage TARGET_NAME) - # Argument parsing - set(options AUTO ALL EXTERNAL PUBLIC INTERFACE PLAIN) - set(single_value_keywords COVERAGE_TARGET_NAME) - set(multi_value_keywords EXCLUDE OBJECTS ARGS) - cmake_parse_arguments( - target_code_coverage "${options}" "${single_value_keywords}" - "${multi_value_keywords}" ${ARGN}) - - # Set the visibility of target functions to PUBLIC, INTERFACE or default to - # PRIVATE. - if(target_code_coverage_PUBLIC) - set(TARGET_VISIBILITY PUBLIC) - set(TARGET_LINK_VISIBILITY PUBLIC) - elseif(target_code_coverage_INTERFACE) - set(TARGET_VISIBILITY INTERFACE) - set(TARGET_LINK_VISIBILITY INTERFACE) - elseif(target_code_coverage_PLAIN) - set(TARGET_VISIBILITY PUBLIC) - set(TARGET_LINK_VISIBILITY) - else() - set(TARGET_VISIBILITY PRIVATE) - set(TARGET_LINK_VISIBILITY PRIVATE) - endif() - - if(NOT target_code_coverage_COVERAGE_TARGET_NAME) - # If a specific name was given, use that instead. - set(target_code_coverage_COVERAGE_TARGET_NAME ${TARGET_NAME}) - endif() - - if(CODE_COVERAGE) - - # Add code coverage instrumentation to the target's linker command - if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" - OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") - target_compile_options(${TARGET_NAME} ${TARGET_VISIBILITY} - -fprofile-instr-generate -fcoverage-mapping) - target_link_options(${TARGET_NAME} ${TARGET_VISIBILITY} - -fprofile-instr-generate -fcoverage-mapping) - elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES - "GNU") - target_compile_options(${TARGET_NAME} ${TARGET_VISIBILITY} -fprofile-arcs - -ftest-coverage) - target_link_libraries(${TARGET_NAME} ${TARGET_LINK_VISIBILITY} gcov) - endif() - - # Targets - get_target_property(target_type ${TARGET_NAME} TYPE) - - # Add shared library to processing for 'all' targets - if(target_type STREQUAL "SHARED_LIBRARY" AND target_code_coverage_ALL) - if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" - OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") - add_custom_target( - ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME} - COMMAND - ${CMAKE_COMMAND} -E echo "-object=$" >> - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list - DEPENDS ccov-preprocessing ${TARGET_NAME}) - - if(NOT TARGET ccov-libs) - message( - FATAL_ERROR - "Calling target_code_coverage with 'ALL' must be after a call to 'add_code_coverage_all_targets'." - ) - endif() - - add_dependencies(ccov-libs - ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}) - endif() - endif() - - # For executables add targets to run and produce output - if(target_type STREQUAL "EXECUTABLE") - if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" - OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") - - # If there are shared objects to also work with, generate the string to - # add them here - foreach(SO_TARGET ${target_code_coverage_OBJECTS}) - # Check to see if the target is a shared object - if(TARGET ${SO_TARGET}) - get_target_property(SO_TARGET_TYPE ${SO_TARGET} TYPE) - if(${SO_TARGET_TYPE} STREQUAL "SHARED_LIBRARY") - set(SO_OBJECTS ${SO_OBJECTS} -object=$) - endif() - endif() - endforeach() - - # Run the executable, generating raw profile data Make the run data - # available for further processing. Separated to allow Windows to run - # this target serially. - add_custom_target( - ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME} - COMMAND - ${CMAKE_COMMAND} -E env - LLVM_PROFILE_FILE=${target_code_coverage_COVERAGE_TARGET_NAME}.profraw - $ ${target_code_coverage_ARGS} - COMMAND - ${CMAKE_COMMAND} -E echo "-object=$" - ${SO_OBJECTS} >> ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list - COMMAND - ${CMAKE_COMMAND} -E echo - "${CMAKE_CURRENT_BINARY_DIR}/${target_code_coverage_COVERAGE_TARGET_NAME}.profraw" - >> ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list - JOB_POOL ccov_serial_pool - DEPENDS ccov-preprocessing ccov-libs ${TARGET_NAME}) - - # Merge the generated profile data so llvm-cov can process it - add_custom_target( - ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME} - COMMAND - ${LLVM_PROFDATA_PATH} merge -sparse - ${target_code_coverage_COVERAGE_TARGET_NAME}.profraw -o - ${target_code_coverage_COVERAGE_TARGET_NAME}.profdata - DEPENDS ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}) - - # Ignore regex only works on LLVM >= 7 - if(LLVM_COV_VERSION VERSION_GREATER_EQUAL "7.0.0") - foreach(EXCLUDE_ITEM ${target_code_coverage_EXCLUDE}) - set(EXCLUDE_REGEX ${EXCLUDE_REGEX} - -ignore-filename-regex='${EXCLUDE_ITEM}') - endforeach() - endif() - - # Print out details of the coverage information to the command line - add_custom_target( - ccov-show-${target_code_coverage_COVERAGE_TARGET_NAME} - COMMAND - ${LLVM_COV_PATH} show $ ${SO_OBJECTS} - -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata - -show-line-counts-or-regions ${EXCLUDE_REGEX} - DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}) - - # Print out a summary of the coverage information to the command line - add_custom_target( - ccov-report-${target_code_coverage_COVERAGE_TARGET_NAME} - COMMAND - ${LLVM_COV_PATH} report $ ${SO_OBJECTS} - -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata - ${EXCLUDE_REGEX} - DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}) - - # Export coverage information so continuous integration tools (e.g. - # Jenkins) can consume it - add_custom_target( - ccov-export-${target_code_coverage_COVERAGE_TARGET_NAME} - COMMAND - ${LLVM_COV_PATH} export $ ${SO_OBJECTS} - -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata - -format="text" ${EXCLUDE_REGEX} > - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}.json - DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}) - - # Generates HTML output of the coverage information for perusal - add_custom_target( - ccov-${target_code_coverage_COVERAGE_TARGET_NAME} - COMMAND - ${LLVM_COV_PATH} show $ ${SO_OBJECTS} - -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata - -show-line-counts-or-regions - -output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME} - -format="html" ${EXCLUDE_REGEX} - DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}) - - elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES - "GNU") - set(COVERAGE_INFO - "${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}.info" - ) - - # Run the executable, generating coverage information - add_custom_target( - ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME} - COMMAND $ ${target_code_coverage_ARGS} - DEPENDS ccov-preprocessing ${TARGET_NAME}) - - # Generate exclusion string for use - foreach(EXCLUDE_ITEM ${target_code_coverage_EXCLUDE}) - set(EXCLUDE_REGEX ${EXCLUDE_REGEX} --remove ${COVERAGE_INFO} - '${EXCLUDE_ITEM}') - endforeach() - - if(EXCLUDE_REGEX) - set(EXCLUDE_COMMAND ${LCOV_PATH} ${EXCLUDE_REGEX} --output-file - ${COVERAGE_INFO}) - else() - set(EXCLUDE_COMMAND ;) - endif() - - if(NOT ${target_code_coverage_EXTERNAL}) - set(EXTERNAL_OPTION --no-external) - endif() - - # Capture coverage data - if(${CMAKE_VERSION} VERSION_LESS "3.17.0") - add_custom_target( - ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME} - COMMAND ${CMAKE_COMMAND} -E remove -f ${COVERAGE_INFO} - COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --zerocounters - COMMAND $ ${target_code_coverage_ARGS} - COMMAND - ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --base-directory - ${CMAKE_SOURCE_DIR} --capture ${EXTERNAL_OPTION} --output-file - ${COVERAGE_INFO} - COMMAND ${EXCLUDE_COMMAND} - DEPENDS ccov-preprocessing ${TARGET_NAME}) - else() - add_custom_target( - ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME} - COMMAND ${CMAKE_COMMAND} -E rm -f ${COVERAGE_INFO} - COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --zerocounters - COMMAND $ ${target_code_coverage_ARGS} - COMMAND - ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --base-directory - ${CMAKE_SOURCE_DIR} --capture ${EXTERNAL_OPTION} --output-file - ${COVERAGE_INFO} - COMMAND ${EXCLUDE_COMMAND} - DEPENDS ccov-preprocessing ${TARGET_NAME}) - endif() - - # Generates HTML output of the coverage information for perusal - add_custom_target( - ccov-${target_code_coverage_COVERAGE_TARGET_NAME} - COMMAND - ${GENHTML_PATH} -o - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME} - ${COVERAGE_INFO} - DEPENDS ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME}) - endif() - - add_custom_command( - TARGET ccov-${target_code_coverage_COVERAGE_TARGET_NAME} - POST_BUILD - COMMAND ; - COMMENT - "Open ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}/index.html in your browser to view the coverage report." - ) - - # AUTO - if(target_code_coverage_AUTO) - if(NOT TARGET ccov) - add_custom_target(ccov) - endif() - add_dependencies(ccov ccov-${target_code_coverage_COVERAGE_TARGET_NAME}) - - if(NOT CMAKE_C_COMPILER_ID MATCHES "GNU" AND NOT CMAKE_CXX_COMPILER_ID - MATCHES "GNU") - if(NOT TARGET ccov-report) - add_custom_target(ccov-report) - endif() - add_dependencies( - ccov-report - ccov-report-${target_code_coverage_COVERAGE_TARGET_NAME}) - endif() - endif() - - # ALL - if(target_code_coverage_ALL) - if(NOT TARGET ccov-all-processing) - message( - FATAL_ERROR - "Calling target_code_coverage with 'ALL' must be after a call to 'add_code_coverage_all_targets'." - ) - endif() - - add_dependencies(ccov-all-processing - ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}) - endif() - endif() - endif() -endfunction() - -# Adds code coverage instrumentation to all targets in the current directory and -# any subdirectories. To add coverage instrumentation to only specific targets, -# use `target_code_coverage`. -function(add_code_coverage) - if(CODE_COVERAGE) - if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" - OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") - add_compile_options(-fprofile-instr-generate -fcoverage-mapping) - add_link_options(-fprofile-instr-generate -fcoverage-mapping) - elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES - "GNU") - add_compile_options(-fprofile-arcs -ftest-coverage) - link_libraries(gcov) - endif() - endif() -endfunction() - -# Adds the 'ccov-all' type targets that calls all targets added via -# `target_code_coverage` with the `ALL` parameter, but merges all the coverage -# data from them into a single large report instead of the numerous smaller -# reports. Also adds the ccov-all-capture Generates an all-merged.info file, for -# use with coverage dashboards (e.g. codecov.io, coveralls). -# ~~~ -# Optional: -# EXCLUDE - Excludes files of the patterns provided from coverage. Note that GCC/lcov excludes by glob pattern, and clang/LLVM excludes via regex! -# ~~~ -function(add_code_coverage_all_targets) - # Argument parsing - set(multi_value_keywords EXCLUDE) - cmake_parse_arguments(add_code_coverage_all_targets "" "" - "${multi_value_keywords}" ${ARGN}) - - if(CODE_COVERAGE) - if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" - OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") - - # Merge the profile data for all of the run executables - if(WIN32) - add_custom_target( - ccov-all-processing - COMMAND - powershell -Command $$FILELIST = Get-Content - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list\; llvm-profdata.exe - merge -o ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata - -sparse $$FILELIST) - else() - add_custom_target( - ccov-all-processing - COMMAND - ${LLVM_PROFDATA_PATH} merge -o - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata -sparse `cat - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list`) - endif() - - # Regex exclude only available for LLVM >= 7 - if(LLVM_COV_VERSION VERSION_GREATER_EQUAL "7.0.0") - foreach(EXCLUDE_ITEM ${add_code_coverage_all_targets_EXCLUDE}) - set(EXCLUDE_REGEX ${EXCLUDE_REGEX} - -ignore-filename-regex='${EXCLUDE_ITEM}') - endforeach() - endif() - - # Print summary of the code coverage information to the command line - if(WIN32) - add_custom_target( - ccov-all-report - COMMAND - powershell -Command $$FILELIST = Get-Content - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list\; llvm-cov.exe - report $$FILELIST - -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata - ${EXCLUDE_REGEX} - DEPENDS ccov-all-processing) - else() - add_custom_target( - ccov-all-report - COMMAND - ${LLVM_COV_PATH} report `cat - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list` - -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata - ${EXCLUDE_REGEX} - DEPENDS ccov-all-processing) - endif() - - # Export coverage information so continuous integration tools (e.g. - # Jenkins) can consume it - add_custom_target( - ccov-all-export - COMMAND - ${LLVM_COV_PATH} export `cat - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list` - -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata - -format="text" ${EXCLUDE_REGEX} > - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/coverage.json - DEPENDS ccov-all-processing) - - # Generate HTML output of all added targets for perusal - if(WIN32) - add_custom_target( - ccov-all - COMMAND - powershell -Command $$FILELIST = Get-Content - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list\; llvm-cov.exe show - $$FILELIST - -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata - -show-line-counts-or-regions - -output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged - -format="html" ${EXCLUDE_REGEX} - DEPENDS ccov-all-processing) - else() - add_custom_target( - ccov-all - COMMAND - ${LLVM_COV_PATH} show `cat - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list` - -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata - -show-line-counts-or-regions - -output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged - -format="html" ${EXCLUDE_REGEX} - DEPENDS ccov-all-processing) - endif() - - elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES - "GNU") - set(COVERAGE_INFO "${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.info") - - # Nothing required for gcov - add_custom_target(ccov-all-processing COMMAND ;) - - # Exclusion regex string creation - set(EXCLUDE_REGEX) - foreach(EXCLUDE_ITEM ${add_code_coverage_all_targets_EXCLUDE}) - set(EXCLUDE_REGEX ${EXCLUDE_REGEX} --remove ${COVERAGE_INFO} - '${EXCLUDE_ITEM}') - endforeach() - - if(EXCLUDE_REGEX) - set(EXCLUDE_COMMAND ${LCOV_PATH} ${EXCLUDE_REGEX} --output-file - ${COVERAGE_INFO}) - else() - set(EXCLUDE_COMMAND ;) - endif() - - # Capture coverage data - if(${CMAKE_VERSION} VERSION_LESS "3.17.0") - add_custom_target( - ccov-all-capture - COMMAND ${CMAKE_COMMAND} -E remove -f ${COVERAGE_INFO} - COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --capture - --output-file ${COVERAGE_INFO} - COMMAND ${EXCLUDE_COMMAND} - DEPENDS ccov-preprocessing ccov-all-processing) - else() - add_custom_target( - ccov-all-capture - COMMAND ${CMAKE_COMMAND} -E rm -f ${COVERAGE_INFO} - COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --capture - --output-file ${COVERAGE_INFO} - COMMAND ${EXCLUDE_COMMAND} - DEPENDS ccov-preprocessing ccov-all-processing) - endif() - - # Generates HTML output of all targets for perusal - add_custom_target( - ccov-all - COMMAND ${GENHTML_PATH} -o ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged - ${COVERAGE_INFO} -p ${CMAKE_SOURCE_DIR} - DEPENDS ccov-all-capture) - - endif() - - add_custom_command( - TARGET ccov-all - POST_BUILD - COMMAND ; - COMMENT - "Open ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged/index.html in your browser to view the coverage report." - ) - endif() -endfunction() diff --git a/cmake/cxx.cmake b/cmake/cxx.cmake deleted file mode 100644 index e69de29b..00000000 From 33f96604ee9052f540fd428ee9c34a22d4d2528e Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 27 Sep 2023 16:38:01 -0400 Subject: [PATCH 040/111] build: cosmetic: remove dead comments --- CMakeLists.txt | 48 -------------------------------------- src/reflect/CMakeLists.txt | 24 ------------------- 2 files changed, 72 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ca908c6..407d67c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,7 +50,6 @@ if(NOT CMAKE_INSTALL_RPATH) set(CMAKE_INSTALL_RPATH $(CMAKE_INSTALL_PREFIX)/lib CACHE STRING "runpath in installed libraries/executables") endif() - # early find_package() experiment find_package(indentlog CONFIG REQUIRED) find_package(refcnt CONFIG REQUIRED) @@ -66,54 +65,7 @@ add_subdirectory(utest) xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) -## ---------------------------------------------------------------- -## cmake export: -## -## populate .cmake files in $CMAKE_INSTALL_LIBDIR/cmake/reflect. -## cmake projects that include this directory in $CMAKE_PREFIX_PATH -## can use -## find_package(reflect REQUIRED) -## and -## target_link_libraries(${sometarget} PUBLIC reflect) -## to use the reflect library -# -#set(XO_PROJECT_CONFIG_VERSION "${XO_PROJECT_NAME}ConfigVersion.cmake") -#set(XO_PROJECT_CONFIG "${XO_PROJECT_NAME}Config.cmake") -# -#include(CMakePackageConfigHelpers) -# -## generates build/reflectConfigVersion.cmake -#write_basic_package_version_file( -# "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG_VERSION}" -# VERSION 0.1 -# COMPATIBILITY AnyNewerVersion -#) -# -## generates build/reflectConfig.cmake -#configure_package_config_file( -# "${PROJECT_SOURCE_DIR}/cmake/${XO_PROJECT_NAME}Config.cmake.in" -# "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG}" -# INSTALL_DESTINATION lib/cmake/${XO_PROJECT_NAME} -#) -# -## creates {reflectTargets.cmake, reflectTargets-noconfig.cmake} in $CMAKE_INSTALL_LIBDIR/cmake/reflect/ -## requires -## install(.. EXPORT reflectTargets ..) -## -#install( -# EXPORT ${XO_PROJECT_NAME}Targets -# DESTINATION lib/cmake/${XO_PROJECT_NAME} -#) -# -## creates {reflectConfigVersion.cmake, reflectConfig.cmake} in $CMAKE_INSTALL_LIBDIR/cmake/reflect/ -#install( -# FILES -# "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG_VERSION}" -# "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG}" -# DESTINATION lib/cmake/${XO_PROJECT_NAME}) - # ---------------------------------------------------------------- # install .hpp files xo_install_include_tree() -#install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/reflect/ DESTINATION include/reflect) diff --git a/src/reflect/CMakeLists.txt b/src/reflect/CMakeLists.txt index bf709478..0b5c710e 100644 --- a/src/reflect/CMakeLists.txt +++ b/src/reflect/CMakeLists.txt @@ -22,32 +22,8 @@ xo_install_library2(${SELF_LIBRARY_NAME}) # ---------------------------------------------------------------- # dependencies: logutil, ... -#target_include_directories( -# ${SELF_LIBRARY_NAME} PUBLIC -# $ -# $ -#) - -#xo_refcnt_dependency(${SELF_LIBRARY_NAME}) -#xo_indentlog_dependency(${SELF_LIBRARY_NAME}) - -#add_dependencies(${SELF_LIBRARY_NAME} indentlog) -# note: can't use find_package() here, -# because find_package() needs to run successfully before -# dependency gets installed. xo_internal_dependency(${SELF_LIBRARY_NAME} indentlog) - -#target_link_libraries(${SELF_LIBRARY_NAME} PUBLIC indentlog) - xo_internal_dependency(${SELF_LIBRARY_NAME} refcnt) -#find_package(refcnt CONFIG REQUIRED) -##add_dependencies(${SELF_LIBRARY_NAME} refcnt) -#target_include_directories( -# ${SELF_LIBRARY_NAME} PUBLIC -# $ -# $ -#) -#target_link_libraries(${SELF_LIBRARY_NAME} PUBLIC refcnt) # ---------------------------------------------------------------- # 3rd party dependency: boost: From b75e3b8fb2d6d5cb755a3b3682a96fbae370ca2e Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 27 Sep 2023 16:38:15 -0400 Subject: [PATCH 041/111] reflect: build: utest streamlining --- utest/CMakeLists.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/utest/CMakeLists.txt b/utest/CMakeLists.txt index 7eae2825..2e1ba51f 100644 --- a/utest/CMakeLists.txt +++ b/utest/CMakeLists.txt @@ -20,14 +20,15 @@ target_code_coverage(${SELF_EXECUTABLE_NAME} AUTO ALL) # since version file will be in build directory, need that directory # to also be included in compiler's include path # -target_include_directories(${SELF_EXECUTABLE_NAME} PUBLIC - ${PROJECT_SOURCE_DIR} - ${PROJECT_BINARY_DIR}) +xo_include_options2(${SELF_EXECUTABLE_NAME}) +#target_include_directories(${SELF_EXECUTABLE_NAME} PUBLIC +# ${PROJECT_SOURCE_DIR} +# ${PROJECT_BINARY_DIR}) # ---------------------------------------------------------------- # internal dependencies: logutil, ... -target_link_libraries(${SELF_EXECUTABLE_NAME} PUBLIC reflect) +xo_internal_dependency(${SELF_EXECUTABLE_NAME} reflect) # ---------------------------------------------------------------- # 3rd part dependency: catch2: From 25712169ee585da4e61bbc78f4d2b0318e28b149 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 27 Sep 2023 16:54:48 -0400 Subject: [PATCH 042/111] reflect: build: streamline --- CMakeLists.txt | 4 ++-- utest/CMakeLists.txt | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 407d67c6..ec69e9c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,8 +51,8 @@ if(NOT CMAKE_INSTALL_RPATH) endif() # early find_package() experiment -find_package(indentlog CONFIG REQUIRED) -find_package(refcnt CONFIG REQUIRED) +#find_package(indentlog CONFIG REQUIRED) +#find_package(refcnt CONFIG REQUIRED) # ---------------------------------------------------------------- # sources diff --git a/utest/CMakeLists.txt b/utest/CMakeLists.txt index 2e1ba51f..75f16ee5 100644 --- a/utest/CMakeLists.txt +++ b/utest/CMakeLists.txt @@ -28,10 +28,11 @@ xo_include_options2(${SELF_EXECUTABLE_NAME}) # ---------------------------------------------------------------- # internal dependencies: logutil, ... -xo_internal_dependency(${SELF_EXECUTABLE_NAME} reflect) +# this won't work. when cmake runs, the Config files for reflect haven't been created yet. +xo_self_dependency(${SELF_EXECUTABLE_NAME} reflect) # ---------------------------------------------------------------- -# 3rd part dependency: catch2: +# 3rd party dependency: catch2: find_package(Catch2 2 REQUIRED) From ee054b6f37c8f042c2e108ad8ca75c14b733517f Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 27 Sep 2023 18:00:16 -0400 Subject: [PATCH 043/111] reflect: build: xo-macros defaults CMAKE_MODULE_PATH --- CMakeLists.txt | 8 -------- 1 file changed, 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ec69e9c5..85a6b475 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,14 +46,6 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "") xo_toplevel_compile_options() -if(NOT CMAKE_INSTALL_RPATH) - set(CMAKE_INSTALL_RPATH $(CMAKE_INSTALL_PREFIX)/lib CACHE STRING "runpath in installed libraries/executables") -endif() - -# early find_package() experiment -#find_package(indentlog CONFIG REQUIRED) -#find_package(refcnt CONFIG REQUIRED) - # ---------------------------------------------------------------- # sources From 3ab6046e2870edf5129417fe890950440ca7ca1c Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 27 Sep 2023 18:12:15 -0400 Subject: [PATCH 044/111] reflect: consolidate CMAKE_CXX_STANDARD setting --- CMakeLists.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 85a6b475..d338456c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,12 +35,6 @@ set(PROJECT_CXX_FLAGS "") add_definitions(${PROJECT_CXX_FLAGS}) -if(NOT CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 20) -endif() - -set(CMAKE_CXX_STANDARD_REQUIRED True) - # always write compile_commands.json set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "") From 5972be91da909a87ee856d1e940605f9f672f21a Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 27 Sep 2023 18:23:57 -0400 Subject: [PATCH 045/111] randomgen: consolidate CMAKE_EXPORT_COMPILE_COMMANDS --- CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d338456c..30a22fb6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,9 +35,6 @@ set(PROJECT_CXX_FLAGS "") add_definitions(${PROJECT_CXX_FLAGS}) -# always write compile_commands.json -set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "") - xo_toplevel_compile_options() # ---------------------------------------------------------------- From 94d77bf809900b2458f87dfe43d8698b1910230c Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 27 Sep 2023 19:42:52 -0400 Subject: [PATCH 046/111] reflect: use xo_add_shared_library() + misc. --- CMakeLists.txt | 2 -- src/reflect/CMakeLists.txt | 16 +--------------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 30a22fb6..0402062d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,10 +29,8 @@ add_code_coverage_all_targets(EXCLUDE /nix/store/* ${PROJECT_SOURCE_DIR}/utest/* # ---------------------------------------------------------------- # c++ settings -set(XO_PROJECT_NAME reflect) set(PROJECT_CXX_FLAGS "") #set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2") - add_definitions(${PROJECT_CXX_FLAGS}) xo_toplevel_compile_options() diff --git a/src/reflect/CMakeLists.txt b/src/reflect/CMakeLists.txt index 0b5c710e..6056ebca 100644 --- a/src/reflect/CMakeLists.txt +++ b/src/reflect/CMakeLists.txt @@ -3,21 +3,7 @@ set(SELF_LIBRARY_NAME reflect) set(SELF_SOURCE_FILES TypeDescr.cpp TypeDescrExtra.cpp TaggedRcptr.cpp atomic/AtomicTdx.cpp pointer/PointerTdx.cpp vector/VectorTdx.cpp struct/StructTdx.cpp struct/StructMember.cpp init_reflect.cpp) -# build shared library 'reflect' -add_library(${SELF_LIBRARY_NAME} SHARED ${SELF_SOURCE_FILES}) - -set_target_properties(${SELF_LIBRARY_NAME} - PROPERTIES - VERSION ${PROJECT_VERSION} - SOVERSION 1) - -# ---------------------------------------------------------------- -# all the errors+warnings! -# -#target_compile_options(${SELF_LIBRARY_NAME} PRIVATE -Werror -Wall -Wextra) -xo_compile_options(${SELF_LIBRARY_NAME}) -xo_include_options2(${SELF_LIBRARY_NAME}) -xo_install_library2(${SELF_LIBRARY_NAME}) +xo_add_shared_library(${SELF_LIBRARY_NAME} ${PROJECT_VERSION} 1 ${SELF_SOURCE_FILES}) # ---------------------------------------------------------------- # dependencies: logutil, ... From 415eeaaca1476754e28f538e0a998515342bb587 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 3 Oct 2023 22:18:16 -0400 Subject: [PATCH 047/111] build tweaks --- .github/workflows/cmake-single-platform.yml | 3 ++- utest/CMakeLists.txt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index a8ce3a72..77554e15 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -20,7 +20,8 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - 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]] diff --git a/utest/CMakeLists.txt b/utest/CMakeLists.txt index 7eae2825..990802e3 100644 --- a/utest/CMakeLists.txt +++ b/utest/CMakeLists.txt @@ -27,7 +27,7 @@ target_include_directories(${SELF_EXECUTABLE_NAME} PUBLIC # ---------------------------------------------------------------- # internal dependencies: logutil, ... -target_link_libraries(${SELF_EXECUTABLE_NAME} PUBLIC reflect) +xo_self_dependency(${SELF_EXECUTABLE_NAME} reflect) # ---------------------------------------------------------------- # 3rd part dependency: catch2: From ee436ca9e96ec2c0f96edbb73e79debf11979867 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 3 Oct 2023 22:22:10 -0400 Subject: [PATCH 048/111] build: technical fix to catch2 dep --- utest/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utest/CMakeLists.txt b/utest/CMakeLists.txt index 09444df5..291f6d16 100644 --- a/utest/CMakeLists.txt +++ b/utest/CMakeLists.txt @@ -33,7 +33,7 @@ xo_self_dependency(${SELF_EXECUTABLE_NAME} reflect) # ---------------------------------------------------------------- # 3rd party dependency: catch2: -find_package(Catch2 2 REQUIRED) +xo_external_target_dependency(${SELF_EXECUTABLE_NAME} Catch2 Catch2::Catch2) # need this so that catch2/include appears in compile_commands.json, # on which lsp integration relies. From d148f3d51fcf7f53903e122b531daadffb31cb07 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 6 Oct 2023 16:39:40 -0400 Subject: [PATCH 049/111] tidy: remove dup decl --- utest/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utest/CMakeLists.txt b/utest/CMakeLists.txt index 291f6d16..8d142b9b 100644 --- a/utest/CMakeLists.txt +++ b/utest/CMakeLists.txt @@ -20,7 +20,7 @@ target_code_coverage(${SELF_EXECUTABLE_NAME} AUTO ALL) # since version file will be in build directory, need that directory # to also be included in compiler's include path # -xo_include_options2(${SELF_EXECUTABLE_NAME}) +#xo_include_options2(${SELF_EXECUTABLE_NAME}) #target_include_directories(${SELF_EXECUTABLE_NAME} PUBLIC # ${PROJECT_SOURCE_DIR} # ${PROJECT_BINARY_DIR}) From c18c848179e10ab770a4c11c2b1a16985bdea70d Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 6 Oct 2023 16:39:53 -0400 Subject: [PATCH 050/111] tidy: c++ indentation --- include/reflect/TaggedPtr.hpp | 182 +++++++++++++++++----------------- 1 file changed, 90 insertions(+), 92 deletions(-) diff --git a/include/reflect/TaggedPtr.hpp b/include/reflect/TaggedPtr.hpp index 653c8ad7..610cb00f 100644 --- a/include/reflect/TaggedPtr.hpp +++ b/include/reflect/TaggedPtr.hpp @@ -7,119 +7,117 @@ #include namespace xo { -namespace reflect { - class TaggedRcptr; /* see [reflect/TaggedRcptr.hpp] */ + namespace reflect { + class TaggedRcptr; /* see [reflect/TaggedRcptr.hpp] */ - class TaggedPtr { - public: - TaggedPtr(TypeDescr td, void * x) : td_{td}, address_{x} {} + class TaggedPtr { + public: + TaggedPtr(TypeDescr td, void * x) : td_{td}, address_{x} {} - static TaggedPtr universal_null() { return TaggedPtr(nullptr, nullptr); } + 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 - */ + /* would be clean to put make() here; + * however it leads to cyclic #include paths, + * so put it elsewhere + */ #ifdef NOT_USING - template - static TaggedPtr make(T * x) { return TaggedPtr(Reflect::require(), x); } + template + static TaggedPtr make(T * x) { return TaggedPtr(Reflect::require(), 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 - static void visit_tree_preorder(TaggedPtr tp, Fn && preorder_visit_fn) { - using std::uint32_t; + /* 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 + static void visit_tree_preorder(TaggedPtr tp, Fn && preorder_visit_fn) { + using std::uint32_t; - preorder_visit_fn(tp); + 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 - static void visit_graph(TaggedPtr tp, Fn && visit_fn) { - std::unordered_set visited_set; + 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_graph_aux(tp, visit_fn, &visited_set); - } /*visit_graph*/ + /* visit object graph. calls preorder_visit_fn() on tp in depth-first + * order. detects and silently prunes duplicate/cyclic references. + */ + template + static void visit_graph(TaggedPtr tp, Fn && visit_fn) { + std::unordered_set visited_set; - TypeDescr td() const { return td_; } - void * address() const { return address_; } - - void assign_td(TypeDescr x) { td_ = x; } - void assign_address(void * x) { address_ = x; } + visit_graph_aux(tp, visit_fn, &visited_set); + } /*visit_graph*/ - bool is_universal_null() const { return (td_ == nullptr) && (address_ == nullptr); } - bool is_vector() const { return td_ && td_->is_vector(); } - bool is_struct() const { return td_ && td_->is_struct(); } + TypeDescr td() const { return td_; } + void * address() const { return address_; } + + void assign_td(TypeDescr x) { td_ = x; } + void assign_address(void * x) { address_ = x; } + + bool is_universal_null() const { return (td_ == nullptr) && (address_ == nullptr); } + bool is_vector() const { return td_ && td_->is_vector(); } + bool is_struct() const { return td_ && td_->is_struct(); } - /* returns pointer-to-T, if in fact this tagged pointer is understood - * to refer to a T-instance; otherwise nullptr - */ - template - T * recover_native() const { return this->td_->recover_native(this->address_); } + /* returns pointer-to-T, if in fact this tagged pointer is understood + * to refer to a T-instance; otherwise nullptr + */ + template + T * recover_native() const { return this->td_->recover_native(this->address_); } - uint32_t n_child() const { - return this->td_->n_child(this->address_); - } /*n_child*/ + uint32_t n_child() const { + return this->td_->n_child(this->address_); + } /*n_child*/ - TaggedPtr get_child(uint32_t i) const { - return this->td_->child_tp(i, this->address_); - } /*get_child*/ + TaggedPtr get_child(uint32_t i) const { + return this->td_->child_tp(i, this->address_); + } /*get_child*/ - /* require: - * - .is_struct() is true - */ - std::string const & struct_member_name(uint32_t i) const { - return this->td_->struct_member_name(i); - } + /* require: + * - .is_struct() is true + */ + std::string const & struct_member_name(uint32_t i) const { + return this->td_->struct_member_name(i); + } - private: - template - static void visit_graph_aux(TaggedPtr tp, - Fn && visit_fn, - std::unordered_set * 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()); + private: + template + static void visit_graph_aux(TaggedPtr tp, + Fn && visit_fn, + std::unordered_set * p_visited_set) + { + if (tp.address() == nullptr) + return; - 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*/ + if (p_visited_set->find(tp.address()) == p_visited_set->end()) { + p_visited_set->insert(tp.address()); - private: - friend class TaggedRcptr; + visit_fn(tp); - 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*/ + 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*/ -} /*namespace reflect*/ + 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 */ - - From af1d2f3535f8e87bd8befb46c046bb73f2e4440d Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 6 Oct 2023 16:40:03 -0400 Subject: [PATCH 051/111] bugfix: cmake config template --- cmake/reflectConfig.cmake.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/reflectConfig.cmake.in b/cmake/reflectConfig.cmake.in index e13a2c54..9c15f36a 100644 --- a/cmake/reflectConfig.cmake.in +++ b/cmake/reflectConfig.cmake.in @@ -1,4 +1,4 @@ @PACKAGE_INIT@ -include("${CMAKE_CURRENT_LIST_DIR}/@XO_PROJECT_NAME@Targets.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") check_required_components("@PROJECT_NAME@") From ba95d0a40c91f89a6a49841191aaee34bd0148bb Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 6 Oct 2023 16:40:18 -0400 Subject: [PATCH 052/111] cosmetic: drop cmake message --- CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0402062d..40c2c41d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,6 @@ cmake_minimum_required(VERSION 3.10) -message(CMAKE_VERSION=${CMAKE_VERSION}) - project(reflect VERSION 0.1) enable_language(CXX) From 6be9037f100b7646dc359003600a4ef6faae04ae Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 6 Oct 2023 16:53:47 -0400 Subject: [PATCH 053/111] reflect: insert xo/ subdir into include path --- include/{ => xo}/reflect/CMakeLists.txt | 0 .../{ => xo}/reflect/EstablishTypeDescr.hpp | 38 ++++---- include/{ => xo}/reflect/Metatype.hpp | 0 include/{ => xo}/reflect/Reflect.hpp | 62 ++++++------- include/{ => xo}/reflect/SelfTagging.hpp | 6 +- include/{ => xo}/reflect/StructReflector.hpp | 90 +++++++++---------- include/{ => xo}/reflect/TaggedPtr.hpp | 3 +- include/{ => xo}/reflect/TaggedRcptr.hpp | 50 +++++------ include/{ => xo}/reflect/TypeDescr.hpp | 2 +- include/{ => xo}/reflect/TypeDescrExtra.hpp | 2 +- include/{ => xo}/reflect/TypeDrivenMap.hpp | 0 include/{ => xo}/reflect/atomic/AtomicTdx.hpp | 2 +- include/{ => xo}/reflect/init_reflect.hpp | 0 .../{ => xo}/reflect/pointer/PointerTdx.hpp | 4 +- .../{ => xo}/reflect/struct/StructMember.hpp | 29 +++--- include/{ => xo}/reflect/struct/StructTdx.hpp | 7 +- include/{ => xo}/reflect/vector/VectorTdx.hpp | 34 ++++--- utest/StructReflector.test.cpp | 8 +- utest/StructTdx.test.cpp | 2 +- utest/VectorTdx.test.cpp | 10 +-- 20 files changed, 172 insertions(+), 177 deletions(-) rename include/{ => xo}/reflect/CMakeLists.txt (100%) rename include/{ => xo}/reflect/EstablishTypeDescr.hpp (60%) rename include/{ => xo}/reflect/Metatype.hpp (100%) rename include/{ => xo}/reflect/Reflect.hpp (84%) rename include/{ => xo}/reflect/SelfTagging.hpp (88%) rename include/{ => xo}/reflect/StructReflector.hpp (64%) rename include/{ => xo}/reflect/TaggedPtr.hpp (98%) rename include/{ => xo}/reflect/TaggedRcptr.hpp (67%) rename include/{ => xo}/reflect/TypeDescr.hpp (99%) rename include/{ => xo}/reflect/TypeDescrExtra.hpp (98%) rename include/{ => xo}/reflect/TypeDrivenMap.hpp (100%) rename include/{ => xo}/reflect/atomic/AtomicTdx.hpp (96%) rename include/{ => xo}/reflect/init_reflect.hpp (100%) rename include/{ => xo}/reflect/pointer/PointerTdx.hpp (96%) rename include/{ => xo}/reflect/struct/StructMember.hpp (92%) rename include/{ => xo}/reflect/struct/StructTdx.hpp (95%) rename include/{ => xo}/reflect/vector/VectorTdx.hpp (75%) diff --git a/include/reflect/CMakeLists.txt b/include/xo/reflect/CMakeLists.txt similarity index 100% rename from include/reflect/CMakeLists.txt rename to include/xo/reflect/CMakeLists.txt diff --git a/include/reflect/EstablishTypeDescr.hpp b/include/xo/reflect/EstablishTypeDescr.hpp similarity index 60% rename from include/reflect/EstablishTypeDescr.hpp rename to include/xo/reflect/EstablishTypeDescr.hpp index 2b0e25a7..32e546d1 100644 --- a/include/reflect/EstablishTypeDescr.hpp +++ b/include/xo/reflect/EstablishTypeDescr.hpp @@ -5,8 +5,8 @@ #pragma once -#include "reflect/TypeDescr.hpp" -#include "reflect/TaggedPtr.hpp" +#include "TypeDescr.hpp" +#include "TaggedPtr.hpp" namespace xo { namespace reflect { @@ -26,28 +26,28 @@ namespace xo { template static TypeDescrW establish() { - TypeDescrW td = TypeDescrBase::require(&typeid(T), - type_name(), - nullptr); + TypeDescrW td = TypeDescrBase::require(&typeid(T), + type_name(), + nullptr); #ifdef NOT_USING - std::function to_self_tp; + std::function to_self_tp; - if (std::is_base_of_v) { - /* T is a descendant of SelfTagging (or T = SelfTagging); - * use SelfTagging.self_tp() - */ - to_self_tp = [](void * x) { return reinterpret_cast(x)->self_tp(); }; - } else { - /* T is not a descendant of SelfTagging. - * want to return - */ - to_self_tp = [td](void * x) { return TaggedPtr(td, x); }; - } + if (std::is_base_of_v) { + /* T is a descendant of SelfTagging (or T = SelfTagging); + * use SelfTagging.self_tp() + */ + to_self_tp = [](void * x) { return reinterpret_cast(x)->self_tp(); }; + } else { + /* T is not a descendant of SelfTagging. + * want to return + */ + to_self_tp = [td](void * x) { return TaggedPtr(td, x); }; + } - td->assign_to_self_tp(to_self_tp); + td->assign_to_self_tp(to_self_tp); #endif - return td; + return td; } }; /*EstablishTypeDescr*/ diff --git a/include/reflect/Metatype.hpp b/include/xo/reflect/Metatype.hpp similarity index 100% rename from include/reflect/Metatype.hpp rename to include/xo/reflect/Metatype.hpp diff --git a/include/reflect/Reflect.hpp b/include/xo/reflect/Reflect.hpp similarity index 84% rename from include/reflect/Reflect.hpp rename to include/xo/reflect/Reflect.hpp index 73ef68f3..a6e6fe5e 100644 --- a/include/reflect/Reflect.hpp +++ b/include/xo/reflect/Reflect.hpp @@ -5,12 +5,12 @@ #pragma once -#include "reflect/SelfTagging.hpp" -#include "reflect/EstablishTypeDescr.hpp" -#include "reflect/atomic/AtomicTdx.hpp" -#include "reflect/pointer/PointerTdx.hpp" -#include "reflect/vector/VectorTdx.hpp" -#include "reflect/struct/StructTdx.hpp" +#include "SelfTagging.hpp" +#include "EstablishTypeDescr.hpp" +#include "atomic/AtomicTdx.hpp" +#include "pointer/PointerTdx.hpp" +#include "vector/VectorTdx.hpp" +#include "struct/StructTdx.hpp" #include "refcnt/Refcounted.hpp" #include #include @@ -22,7 +22,7 @@ namespace xo { class EstablishTdx { public: static std::unique_ptr make() { return AtomicTdx::make(); } - }; /*EstablishTdx*/ + }; /*EstablishTdx*/ // ----- xo::ref::rp ----- @@ -34,7 +34,7 @@ namespace xo { }; /*EstablishTdx*/ // ----- std::array ----- - + /* definition provide after decl for Reflect {} below */ template class EstablishTdx> { @@ -73,11 +73,11 @@ namespace xo { class TaggedPtrMaker { public: static TaggedPtr make_tp(SelfTagging * x) { - return x->self_tp(); + return x->self_tp(); } /*make_tp*/ static TaggedRcptr make_rctp(SelfTagging * x) { - return x->self_tp(); + return x->self_tp(); } /*make_rctp*/ }; /*TaggedPtrMaker*/ @@ -113,35 +113,35 @@ namespace xo { * implemented in specialized header (like [reflect/struct/VectorTdx.hpp]) to * refer to reflection info for T without having to pull in all the * headers needed to properly reflect T (like this [reflect/Reflect.hpp]) - * + * */ template static TypeDescrW require() { - TypeDescrW retval_td = EstablishTypeDescr::establish(); + TypeDescrW retval_td = EstablishTypeDescr::establish(); - /* mark TypeDescr for T as complete (even though it isn't quite yet), - * so that when we encounter recursive types, reflection terminates. - * For example consider type resulting from code like - * - * typename T; - * using T = std::vector; - * - */ - if (retval_td->mark_complete()) { - /* control here on 2nd+later calls to require(). - * in principle can immediately short-circuit. - */ - } else { - /* control comes here the first time require() runs */ + /* mark TypeDescr for T as complete (even though it isn't quite yet), + * so that when we encounter recursive types, reflection terminates. + * For example consider type resulting from code like + * + * typename T; + * using T = std::vector; + * + */ + if (retval_td->mark_complete()) { + /* control here on 2nd+later calls to require(). + * in principle can immediately short-circuit. + */ + } else { + /* control comes here the first time require() runs */ - auto final_tdx = EstablishTdx::make(); + auto final_tdx = EstablishTdx::make(); - retval_td->assign_tdextra(std::move(final_tdx)); + retval_td->assign_tdextra(std::move(final_tdx)); - /* also need to require for each child */ - } + /* also need to require for each child */ + } - return retval_td; + return retval_td; } /*require*/ /* Use: diff --git a/include/reflect/SelfTagging.hpp b/include/xo/reflect/SelfTagging.hpp similarity index 88% rename from include/reflect/SelfTagging.hpp rename to include/xo/reflect/SelfTagging.hpp index 29517e33..e188b5c2 100644 --- a/include/reflect/SelfTagging.hpp +++ b/include/xo/reflect/SelfTagging.hpp @@ -5,9 +5,9 @@ #pragma once -#include "refcnt/Refcounted.hpp" -#include "reflect/TypeDescr.hpp" -#include "reflect/TaggedRcptr.hpp" +#include "Refcounted.hpp" +#include "TypeDescr.hpp" +#include "TaggedRcptr.hpp" namespace xo { namespace reflect { diff --git a/include/reflect/StructReflector.hpp b/include/xo/reflect/StructReflector.hpp similarity index 64% rename from include/reflect/StructReflector.hpp rename to include/xo/reflect/StructReflector.hpp index f2237578..750b8669 100644 --- a/include/reflect/StructReflector.hpp +++ b/include/xo/reflect/StructReflector.hpp @@ -2,10 +2,10 @@ #pragma once -#include "reflect/Reflect.hpp" -#include "reflect/TypeDescr.hpp" -#include "reflect/struct/StructMember.hpp" -#include "reflect/struct/StructTdx.hpp" +#include "Reflect.hpp" +#include "TypeDescr.hpp" +#include "struct/StructMember.hpp" +#include "struct/StructTdx.hpp" #include namespace xo { @@ -16,7 +16,7 @@ namespace xo { template struct SelfTagger { static TaggedPtr self_tp(void * object) { - return (reinterpret_cast(object))->self_tp(); + return (reinterpret_cast(object))->self_tp(); } }; @@ -35,7 +35,7 @@ namespace xo { * REFLECT_LITERAL_MEMBER(sr, y_); * * // optional: regardless, reflection will be completed when sr goes out of scope - * sr.require_complete(); + * sr.require_complete(); */ template class StructReflector { @@ -45,68 +45,68 @@ namespace xo { public: StructReflector() : td_{EstablishTypeDescr::establish()} {} ~StructReflector() { - this->require_complete(); + this->require_complete(); } - + bool is_complete() const { return s_reflected_flag; } bool is_incomplete() const { return !s_reflected_flag; } template void reflect_member(std::string const & member_name, - MemberT OwnerT::* member_addr) { + MemberT OwnerT::* member_addr) { - auto accessor - (GeneralStructMemberAccessor::make(member_addr)); + auto accessor + (GeneralStructMemberAccessor::make(member_addr)); - /* used to do this in GeneralStructMemberAccessor<> ctor, - * but that introduces #include cycle - */ - Reflect::require(); + /* used to do this in GeneralStructMemberAccessor<> ctor, + * but that introduces #include cycle + */ + Reflect::require(); - this->member_v_.emplace_back(member_name, std::move(accessor)); + this->member_v_.emplace_back(member_name, std::move(accessor)); } /*reflect_member*/ void require_complete() { - if(!s_reflected_flag) { - s_reflected_flag = true; + if(!s_reflected_flag) { + s_reflected_flag = true; - constexpr bool have_to_self_tp = std::is_base_of_v; + constexpr bool have_to_self_tp = std::is_base_of_v; - /* if self-tagging, can use .self_tp() to get most-derived tagged pointer */ - auto to_self_tp_fn - = ([](void * object) - { - return SelfTagger::self_tp(object); - }); + /* if self-tagging, can use .self_tp() to get most-derived tagged pointer */ + auto to_self_tp_fn + = ([](void * object) + { + return SelfTagger::self_tp(object); + }); - auto tdx = StructTdx::make(std::move(this->member_v_), - have_to_self_tp, - to_self_tp_fn); + auto tdx = StructTdx::make(std::move(this->member_v_), + have_to_self_tp, + to_self_tp_fn); - this->td_->assign_tdextra(std::move(tdx)); - } + this->td_->assign_tdextra(std::move(tdx)); + } } /*complete*/ - + template void adopt_ancestors() { - assert(Reflect::is_reflected()); + assert(Reflect::is_reflected()); - TypeDescr ancestor_td = Reflect::require(); + TypeDescr ancestor_td = Reflect::require(); - /* requires that reflection of AncestorT has completed */ - { - assert(ancestor_td->is_struct()); - assert(ancestor_td->complete_flag()); - } + /* 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); + /* 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()); - } + this->member_v_.push_back(member.for_descendant()); + } } /*adopt_ancestors*/ private: diff --git a/include/reflect/TaggedPtr.hpp b/include/xo/reflect/TaggedPtr.hpp similarity index 98% rename from include/reflect/TaggedPtr.hpp rename to include/xo/reflect/TaggedPtr.hpp index 610cb00f..7bb520aa 100644 --- a/include/reflect/TaggedPtr.hpp +++ b/include/xo/reflect/TaggedPtr.hpp @@ -2,8 +2,7 @@ #pragma once -#include "reflect/TypeDescr.hpp" -//#include "reflect/EstablishTypeDescr.hpp" +#include "TypeDescr.hpp" #include namespace xo { diff --git a/include/reflect/TaggedRcptr.hpp b/include/xo/reflect/TaggedRcptr.hpp similarity index 67% rename from include/reflect/TaggedRcptr.hpp rename to include/xo/reflect/TaggedRcptr.hpp index 9ca8b15f..e31cffb3 100644 --- a/include/reflect/TaggedRcptr.hpp +++ b/include/xo/reflect/TaggedRcptr.hpp @@ -5,7 +5,7 @@ #pragma once -#include "reflect/TaggedPtr.hpp" +#include "TaggedPtr.hpp" // causes #include cycle, reflect/Reflect.hpp includes this header //#include "reflect/Reflect.hpp" #include "refcnt/Refcounted.hpp" @@ -20,22 +20,22 @@ namespace xo { class TaggedRcptr : public TaggedPtr { public: using Refcount = ref::Refcount; - + public: TaggedRcptr(TypeDescr td, Refcount * x) : TaggedPtr(td, x) { - ref::intrusive_ptr_add_ref(x); + ref::intrusive_ptr_add_ref(x); } TaggedRcptr(TaggedRcptr const & x) : TaggedPtr(x) { - ref::intrusive_ptr_add_ref(x.rc_address()); + 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); + /* 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()); + ref::intrusive_ptr_release(this->rc_address()); } /* causes #include cycle, see [reflect/Reflect.hpp] */ @@ -46,37 +46,37 @@ namespace xo { #endif Refcount * rc_address() const { - return reinterpret_cast(this->address()); + return reinterpret_cast(this->address()); } /*rc_address*/ TaggedRcptr & operator=(TaggedRcptr const & rhs) { - Refcount * x = rhs.rc_address(); - Refcount * old = this->rc_address(); + Refcount * x = rhs.rc_address(); + Refcount * old = this->rc_address(); - TaggedPtr::operator=(rhs); + TaggedPtr::operator=(rhs); - if (x != old) { - intrusive_ptr_release(old); - intrusive_ptr_add_ref(x); - } + if (x != old) { + intrusive_ptr_release(old); + intrusive_ptr_add_ref(x); + } - return *this; + 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_); + /* 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; + 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; diff --git a/include/reflect/TypeDescr.hpp b/include/xo/reflect/TypeDescr.hpp similarity index 99% rename from include/reflect/TypeDescr.hpp rename to include/xo/reflect/TypeDescr.hpp index 08614071..da59c007 100644 --- a/include/reflect/TypeDescr.hpp +++ b/include/xo/reflect/TypeDescr.hpp @@ -3,7 +3,7 @@ #pragma once //#include "reflect/atomic/AtomicTdx.hpp" -#include "reflect/TypeDescrExtra.hpp" +#include "TypeDescrExtra.hpp" #include "cxxutil/demangle.hpp" #include #include diff --git a/include/reflect/TypeDescrExtra.hpp b/include/xo/reflect/TypeDescrExtra.hpp similarity index 98% rename from include/reflect/TypeDescrExtra.hpp rename to include/xo/reflect/TypeDescrExtra.hpp index 2d2be7fa..824cd5d4 100644 --- a/include/reflect/TypeDescrExtra.hpp +++ b/include/xo/reflect/TypeDescrExtra.hpp @@ -2,7 +2,7 @@ #pragma once -#include "reflect/Metatype.hpp" +#include "Metatype.hpp" #include /* note: this file #include'd into TypeDescr.hpp */ #include diff --git a/include/reflect/TypeDrivenMap.hpp b/include/xo/reflect/TypeDrivenMap.hpp similarity index 100% rename from include/reflect/TypeDrivenMap.hpp rename to include/xo/reflect/TypeDrivenMap.hpp diff --git a/include/reflect/atomic/AtomicTdx.hpp b/include/xo/reflect/atomic/AtomicTdx.hpp similarity index 96% rename from include/reflect/atomic/AtomicTdx.hpp rename to include/xo/reflect/atomic/AtomicTdx.hpp index 7b2e042d..98e10cfe 100644 --- a/include/reflect/atomic/AtomicTdx.hpp +++ b/include/xo/reflect/atomic/AtomicTdx.hpp @@ -2,7 +2,7 @@ #pragma once -#include "reflect/TypeDescrExtra.hpp" +#include "xo/reflect/TypeDescrExtra.hpp" //#include "reflect/TaggedPtr.hpp" #include diff --git a/include/reflect/init_reflect.hpp b/include/xo/reflect/init_reflect.hpp similarity index 100% rename from include/reflect/init_reflect.hpp rename to include/xo/reflect/init_reflect.hpp diff --git a/include/reflect/pointer/PointerTdx.hpp b/include/xo/reflect/pointer/PointerTdx.hpp similarity index 96% rename from include/reflect/pointer/PointerTdx.hpp rename to include/xo/reflect/pointer/PointerTdx.hpp index d2d3b868..327456cf 100644 --- a/include/reflect/pointer/PointerTdx.hpp +++ b/include/xo/reflect/pointer/PointerTdx.hpp @@ -5,8 +5,8 @@ #pragma once -#include "reflect/TypeDescrExtra.hpp" -#include "reflect/EstablishTypeDescr.hpp" +#include "xo/reflect/TypeDescrExtra.hpp" +#include "xo/reflect/EstablishTypeDescr.hpp" #include "indentlog/scope.hpp" namespace xo { diff --git a/include/reflect/struct/StructMember.hpp b/include/xo/reflect/struct/StructMember.hpp similarity index 92% rename from include/reflect/struct/StructMember.hpp rename to include/xo/reflect/struct/StructMember.hpp index eaddf3b3..3c7bab62 100644 --- a/include/reflect/struct/StructMember.hpp +++ b/include/xo/reflect/struct/StructMember.hpp @@ -2,10 +2,9 @@ #pragma once -#include "reflect/TypeDescr.hpp" -#include "reflect/EstablishTypeDescr.hpp" -//#include "reflect/Reflect.hpp" -#include "reflect/TaggedPtr.hpp" +#include "xo/reflect/TypeDescr.hpp" +#include "xo/reflect/EstablishTypeDescr.hpp" +#include "xo/reflect/TaggedPtr.hpp" #include #include @@ -14,7 +13,7 @@ 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; @@ -30,7 +29,7 @@ namespace reflect { * .member_td() => Reflect::require(); */ virtual TypeDescr member_td() const = 0; - + /* get address of a particular member, given parent address */ virtual void * address(void * struct_addr) const = 0; @@ -63,7 +62,7 @@ namespace reflect { public: GeneralStructMemberAccessor(Memptr memptr) : member_td_{EstablishTypeDescr::establish()}, - memptr_{memptr} {} + memptr_{memptr} {} GeneralStructMemberAccessor(GeneralStructMemberAccessor const & x) = default; virtual ~GeneralStructMemberAccessor() = default; @@ -78,7 +77,7 @@ namespace reflect { return &(owner_addr->*memptr_); } /*address_impl*/ - + // ----- Inherited from AbstractStructMemberAccessor ----- #ifdef OBSOLETE @@ -102,7 +101,7 @@ namespace reflect { virtual std::unique_ptr clone() const override { return std::unique_ptr - (new GeneralStructMemberAccessor(*this)); + (new GeneralStructMemberAccessor(*this)); } /*clone*/ private: @@ -142,7 +141,7 @@ namespace reflect { static std::unique_ptr adopt(std::unique_ptr ancestor_accessor) { return std::unique_ptr - (new AncestorStructMemberAccessor(std::move(ancestor_accessor))); + (new AncestorStructMemberAccessor(std::move(ancestor_accessor))); } /*adopt*/ void * address_impl(StructT * self_addr) const { @@ -171,7 +170,7 @@ namespace reflect { virtual std::unique_ptr clone() const override { return std::unique_ptr - (new AncestorStructMemberAccessor(std::move(this->ancestor_accessor_->clone()))); + (new AncestorStructMemberAccessor(std::move(this->ancestor_accessor_->clone()))); } /*clone*/ private: @@ -186,11 +185,11 @@ namespace reflect { public: StructMember() = default; StructMember(std::string const & name, - std::unique_ptr accessor) + std::unique_ptr accessor) : member_name_{name}, accessor_{std::move(accessor)} {} StructMember(StructMember && x) : member_name_{std::move(x.member_name_)}, - accessor_{std::move(x.accessor_)} {} + accessor_{std::move(x.accessor_)} {} static StructMember null(); @@ -210,8 +209,8 @@ namespace reflect { assert(EstablishTypeDescr::establish() == this->get_struct_td()); return StructMember(this->member_name(), - std::move(AncestorStructMemberAccessor::adopt - (std::move(this->accessor_->clone())))); + std::move(AncestorStructMemberAccessor::adopt + (std::move(this->accessor_->clone())))); } /*for_descendant*/ StructMember & operator=(StructMember && x) { diff --git a/include/reflect/struct/StructTdx.hpp b/include/xo/reflect/struct/StructTdx.hpp similarity index 95% rename from include/reflect/struct/StructTdx.hpp rename to include/xo/reflect/struct/StructTdx.hpp index 5e170ef7..594de2d5 100644 --- a/include/reflect/struct/StructTdx.hpp +++ b/include/xo/reflect/struct/StructTdx.hpp @@ -2,9 +2,10 @@ #pragma once -#include "reflect/TypeDescrExtra.hpp" -#include "reflect/TaggedPtr.hpp" -#include "reflect/struct/StructMember.hpp" +#include "xo/reflect/TypeDescrExtra.hpp" +#include "xo/reflect/TaggedPtr.hpp" +#include "StructMember.hpp" +//#include "xo/reflect/struct/StructMember.hpp" #include #include #include diff --git a/include/reflect/vector/VectorTdx.hpp b/include/xo/reflect/vector/VectorTdx.hpp similarity index 75% rename from include/reflect/vector/VectorTdx.hpp rename to include/xo/reflect/vector/VectorTdx.hpp index 4f3309c2..944c451c 100644 --- a/include/reflect/vector/VectorTdx.hpp +++ b/include/xo/reflect/vector/VectorTdx.hpp @@ -5,12 +5,8 @@ #pragma once -#include "reflect/TypeDescrExtra.hpp" -//#include "reflect/TaggedPtr.hpp" -#include "reflect/EstablishTypeDescr.hpp" -//#include "reflect/TaggedPtr.hpp" -//#include -//#include +#include "xo/reflect/TypeDescrExtra.hpp" +#include "xo/reflect/EstablishTypeDescr.hpp" namespace xo { namespace reflect { @@ -42,19 +38,19 @@ namespace xo { using target_t = VectorT; static std::unique_ptr make() { - return std::unique_ptr(new StlVectorTdx()); + return std::unique_ptr(new StlVectorTdx()); } /*make*/ virtual uint32_t n_child(void * object) const override { - target_t * vec = reinterpret_cast(object); + target_t * vec = reinterpret_cast(object); - return vec->size(); + return vec->size(); } /*n_child*/ virtual TaggedPtr child_tp(uint32_t i, void * object) const override { - target_t * vec = reinterpret_cast(object); + target_t * vec = reinterpret_cast(object); - return establish_most_derived_tp(&((*vec)[i])); + return establish_most_derived_tp(&((*vec)[i])); } /*child_tp*/ }; /*StlVectorTdx*/ @@ -66,9 +62,9 @@ namespace xo { template using StdArrayTdx = StlVectorTdx>; - + // ----- std::vector ----- - + /* coordinates with EstablishTdx>::make() * see [reflect/Reflect.hpp] */ @@ -76,21 +72,21 @@ namespace xo { class StdVectorTdx : public VectorTdx { public: using target_t = std::vector; - + static std::unique_ptr make() { - return std::unique_ptr(new StdVectorTdx()); + return std::unique_ptr(new StdVectorTdx()); } /*make*/ virtual uint32_t n_child(void * object) const override { - target_t * vec = reinterpret_cast(object); + target_t * vec = reinterpret_cast(object); - return vec->size(); + return vec->size(); } /*n_child*/ virtual TaggedPtr child_tp(uint32_t i, void * object) const override { - target_t * vec = reinterpret_cast(object); + target_t * vec = reinterpret_cast(object); - return establish_most_derived_tp(&((*vec)[i])); + return establish_most_derived_tp(&((*vec)[i])); } }; /*StdVectorTdx*/ } /*namespace reflect*/ diff --git a/utest/StructReflector.test.cpp b/utest/StructReflector.test.cpp index 5bf87ade..4ab0772d 100644 --- a/utest/StructReflector.test.cpp +++ b/utest/StructReflector.test.cpp @@ -3,12 +3,12 @@ * author: Roland Conybeare, Aug 2022 */ -#include "reflect/Reflect.hpp" -#include "reflect/StructReflector.hpp" +#include "xo/reflect/Reflect.hpp" +#include "xo/reflect/StructReflector.hpp" #include #define STRINGIFY(x) #x - + namespace xo { using xo::reflect::Reflect; using xo::reflect::TaggedPtr; @@ -129,7 +129,7 @@ namespace xo { REQUIRE(tp.get_child(2).td() == Reflect::require()); 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); diff --git a/utest/StructTdx.test.cpp b/utest/StructTdx.test.cpp index c20e2139..cb845e18 100644 --- a/utest/StructTdx.test.cpp +++ b/utest/StructTdx.test.cpp @@ -3,7 +3,7 @@ * author: Roland Conybeare, Aug 2022 */ -#include "reflect/Reflect.hpp" +#include "xo/reflect/Reflect.hpp" #include namespace xo { diff --git a/utest/VectorTdx.test.cpp b/utest/VectorTdx.test.cpp index 3836b4f7..00f30a48 100644 --- a/utest/VectorTdx.test.cpp +++ b/utest/VectorTdx.test.cpp @@ -3,7 +3,7 @@ * author: Roland Conybeare, Aug 2022 */ -#include "reflect/Reflect.hpp" +#include "xo/reflect/Reflect.hpp" #include namespace xo { @@ -81,7 +81,7 @@ namespace xo { REQUIRE(tp0.td()->metatype() == Metatype::mt_atomic); REQUIRE(tp0.recover_native() == &(v[0])); REQUIRE(tp0.n_child() == 0); - + TaggedPtr tp1 = tp.get_child(1); REQUIRE(tp1.td()->complete_flag()); @@ -91,7 +91,7 @@ namespace xo { REQUIRE(tp1.td()->metatype() == Metatype::mt_atomic); REQUIRE(tp1.recover_native() == &(v[1])); REQUIRE(tp1.n_child() == 0); - } /*TEST(std-vector-reflect-two)*/ + } /*TEST(std-vector-reflect-two)*/ // ----- std::array ----- @@ -163,7 +163,7 @@ namespace xo { REQUIRE(tp0.td()->metatype() == Metatype::mt_atomic); REQUIRE(tp0.recover_native() == &(v[0])); REQUIRE(tp0.n_child() == 0); - + TaggedPtr tp1 = tp.get_child(1); REQUIRE(tp1.td()->complete_flag()); @@ -173,7 +173,7 @@ namespace xo { REQUIRE(tp1.td()->metatype() == Metatype::mt_atomic); REQUIRE(tp1.recover_native() == &(v[1])); REQUIRE(tp1.n_child() == 0); - } /*TEST(std-array-reflect-two)*/ + } /*TEST(std-array-reflect-two)*/ } /*namespace ut*/ } /*namespace xo*/ From c8280cb8c4e00d0c0973d245726b1c31ed18cd25 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 6 Oct 2023 17:05:08 -0400 Subject: [PATCH 054/111] document compile_commands.json symlink + .gitignore --- .gitignore | 2 ++ README.md | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 6637741b..eff45bd2 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ .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 diff --git a/README.md b/README.md index 0cd50f02..3c5d34be 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ $ cd reflect $ mkdir build $ cd build +$ INSTALL_PREFIX=/usr/local # or wherever you prefer $ cmake -DCMAKE_MODULE_PATH=${INSTALL_PREFIX}/share/cmake -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} .. $ make $ make install @@ -12,8 +13,14 @@ $ make install ### build for unit test coverage ``` -$ cd refcnt +$ cd xo-reflect $ mkdir build-ccov $ cd build-ccov $ cmake -DCMAKE_MODULE_PATH=${INSTALL_PREFIX}/share/cmake -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} -DCODE_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug .. ``` + +### 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 +``` From 9718be824b6270f445ec79f62c6dfc89ba1dde9c Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 6 Oct 2023 17:05:28 -0400 Subject: [PATCH 055/111] cosmetic: indentation --- include/xo/reflect/Reflect.hpp | 352 +++++++++++++++---------------- include/xo/reflect/TypeDescr.hpp | 119 ++++++----- 2 files changed, 235 insertions(+), 236 deletions(-) diff --git a/include/xo/reflect/Reflect.hpp b/include/xo/reflect/Reflect.hpp index a6e6fe5e..478078ad 100644 --- a/include/xo/reflect/Reflect.hpp +++ b/include/xo/reflect/Reflect.hpp @@ -17,219 +17,219 @@ #include // for std::pair<> namespace xo { - namespace reflect { - template - class EstablishTdx { - public: - static std::unique_ptr make() { return AtomicTdx::make(); } - }; /*EstablishTdx*/ + namespace reflect { + template + class EstablishTdx { + public: + static std::unique_ptr make() { return AtomicTdx::make(); } + }; /*EstablishTdx*/ - // ----- xo::ref::rp ----- + // ----- xo::ref::rp ----- - /* definition provide after decl for Reflect {} below */ - template - class EstablishTdx> { - public: - static std::unique_ptr make(); - }; /*EstablishTdx*/ + /* definition provide after decl for Reflect {} below */ + template + class EstablishTdx> { + public: + static std::unique_ptr make(); + }; /*EstablishTdx*/ - // ----- std::array ----- + // ----- std::array ----- - /* definition provide after decl for Reflect {} below */ - template - class EstablishTdx> { - public: - static std::unique_ptr make(); - }; /*EstablishTdx*/ + /* definition provide after decl for Reflect {} below */ + template + class EstablishTdx> { + public: + static std::unique_ptr make(); + }; /*EstablishTdx*/ - // ----- std::vector ----- + // ----- std::vector ----- - /* definition provide after decl for Reflect {} below */ - template - class EstablishTdx> { - public: - static std::unique_ptr make(); - }; /*EstablishTdx*/ + /* definition provide after decl for Reflect {} below */ + template + class EstablishTdx> { + public: + static std::unique_ptr make(); + }; /*EstablishTdx*/ - // ----- std::pair ----- + // ----- std::pair ----- - /* definition provide after decl for Reflect {} below */ - template - class EstablishTdx> { - public: - static std::unique_ptr make(); - }; /*EstablishTdx*/ + /* definition provide after decl for Reflect {} below */ + template + class EstablishTdx> { + public: + static std::unique_ptr make(); + }; /*EstablishTdx*/ - // ----- MakeTagged ----- + // ----- MakeTagged ----- - template - class TaggedPtrMaker { - public: - static TaggedPtr make_tp(T * x); - static TaggedRcptr make_rctp(T * x); - }; + template + class TaggedPtrMaker { + public: + static TaggedPtr make_tp(T * x); + static TaggedRcptr make_rctp(T * x); + }; - template<> - class TaggedPtrMaker { - public: - static TaggedPtr make_tp(SelfTagging * x) { - return x->self_tp(); - } /*make_tp*/ + template<> + class TaggedPtrMaker { + public: + static TaggedPtr make_tp(SelfTagging * x) { + return x->self_tp(); + } /*make_tp*/ - static TaggedRcptr make_rctp(SelfTagging * x) { - return x->self_tp(); - } /*make_rctp*/ - }; /*TaggedPtrMaker*/ + static TaggedRcptr make_rctp(SelfTagging * x) { + return x->self_tp(); + } /*make_rctp*/ + }; /*TaggedPtrMaker*/ - // ----- Reflect ----- + // ----- Reflect ----- - class Reflect { - public: - /* Use: - * using mytype = ...; - * if (Reflect::is_reflected()) { ... } - */ - template - static bool is_reflected() { return TypeDescrBase::is_reflected(&typeid(T)); } + class Reflect { + public: + /* Use: + * using mytype = ...; + * if (Reflect::is_reflected()) { ... } + */ + template + static bool is_reflected() { return TypeDescrBase::is_reflected(&typeid(T)); } - /* Use: - * using mytype = ...; - * TypeDescrW td = Reflect::require(); - * - * Note: - * To avoid cyclic header dependencies - * (between EstablishTypeDescr.hpp <-> {vector/VectorTdx.hpp etc.}, - * we use a 2-stage setup process: - * - * 1. EstablishTypeDescr::establish() creates a TypeDescr* object - * with lowest-common-denominator .tdextra AtomicTdx. - * (see [reflect/EstablishTypeDescr.hpp]) - * - * 2. Reflect::require() upgrades .tdextra to suitable implementation - * depending on T; this means also need to visit reflection info - * (TypeDescr objects) for nested types to upgrade them too. - * - * This allows template-fu for a compound type (like std::vector), - * implemented in specialized header (like [reflect/struct/VectorTdx.hpp]) to - * refer to reflection info for T without having to pull in all the - * headers needed to properly reflect T (like this [reflect/Reflect.hpp]) - * - */ - template - static TypeDescrW require() { - TypeDescrW retval_td = EstablishTypeDescr::establish(); + /* Use: + * using mytype = ...; + * TypeDescrW td = Reflect::require(); + * + * Note: + * To avoid cyclic header dependencies + * (between EstablishTypeDescr.hpp <-> {vector/VectorTdx.hpp etc.}, + * we use a 2-stage setup process: + * + * 1. EstablishTypeDescr::establish() creates a TypeDescr* object + * with lowest-common-denominator .tdextra AtomicTdx. + * (see [reflect/EstablishTypeDescr.hpp]) + * + * 2. Reflect::require() upgrades .tdextra to suitable implementation + * depending on T; this means also need to visit reflection info + * (TypeDescr objects) for nested types to upgrade them too. + * + * This allows template-fu for a compound type (like std::vector), + * implemented in specialized header (like [reflect/struct/VectorTdx.hpp]) to + * refer to reflection info for T without having to pull in all the + * headers needed to properly reflect T (like this [reflect/Reflect.hpp]) + * + */ + template + static TypeDescrW require() { + TypeDescrW retval_td = EstablishTypeDescr::establish(); - /* mark TypeDescr for T as complete (even though it isn't quite yet), - * so that when we encounter recursive types, reflection terminates. - * For example consider type resulting from code like - * - * typename T; - * using T = std::vector; - * - */ - if (retval_td->mark_complete()) { - /* control here on 2nd+later calls to require(). - * in principle can immediately short-circuit. - */ - } else { - /* control comes here the first time require() runs */ + /* mark TypeDescr for T as complete (even though it isn't quite yet), + * so that when we encounter recursive types, reflection terminates. + * For example consider type resulting from code like + * + * typename T; + * using T = std::vector; + * + */ + if (retval_td->mark_complete()) { + /* control here on 2nd+later calls to require(). + * in principle can immediately short-circuit. + */ + } else { + /* control comes here the first time require() runs */ - auto final_tdx = EstablishTdx::make(); + auto final_tdx = EstablishTdx::make(); - retval_td->assign_tdextra(std::move(final_tdx)); + retval_td->assign_tdextra(std::move(final_tdx)); - /* also need to require for each child */ - } + /* also need to require for each child */ + } - return retval_td; - } /*require*/ + return retval_td; + } /*require*/ - /* Use: - * T * xyz = ...; - * TaggedPtr xyz_tp = Reflect::make_tp(xyz); - */ - template - static TaggedPtr make_tp(T * x) { return TaggedPtrMaker::make_tp(x); } + /* Use: + * T * xyz = ...; + * TaggedPtr xyz_tp = Reflect::make_tp(xyz); + */ + template + static TaggedPtr make_tp(T * x) { return TaggedPtrMaker::make_tp(x); } - template - static TaggedRcptr make_rctp(T * x) { return TaggedPtrMaker::make_rctp(x); } - }; /*Reflect*/ + template + static TaggedRcptr make_rctp(T * x) { return TaggedPtrMaker::make_rctp(x); } + }; /*Reflect*/ - // ----- MakeTagged ----- + // ----- MakeTagged ----- - template - TaggedPtr - TaggedPtrMaker::make_tp(T * x) { - return TaggedPtr(Reflect::require(), x); - } /*make_tp*/ + template + TaggedPtr + TaggedPtrMaker::make_tp(T * x) { + return TaggedPtr(Reflect::require(), x); + } /*make_tp*/ - template - TaggedRcptr - TaggedPtrMaker::make_rctp(T * x) { - return TaggedRcptr(Reflect::require(), x); - } /*make_rctp*/ + template + TaggedRcptr + TaggedPtrMaker::make_rctp(T * x) { + return TaggedRcptr(Reflect::require(), x); + } /*make_rctp*/ - // ----- xo::ref::rp ----- + // ----- xo::ref::rp ----- - /* declared above before - * class Reflect { .. } - */ - template - std::unique_ptr - EstablishTdx>::make() { - /* need to ensure Object is property reflected. - * - * In practice must be a class type, since has to store refcount - * + supply assoc'd incr/decr methods - */ - Reflect::require(); + /* declared above before + * class Reflect { .. } + */ + template + std::unique_ptr + EstablishTdx>::make() { + /* need to ensure Object is property reflected. + * + * In practice must be a class type, since has to store refcount + * + supply assoc'd incr/decr methods + */ + Reflect::require(); - return RefPointerTdx>::make(); - } /*make*/ + return RefPointerTdx>::make(); + } /*make*/ - // ----- std::array ----- + // ----- std::array ----- - /* declared above before - * class Reflect { .. } - */ - template - std::unique_ptr - EstablishTdx>::make() { - /* need to ensure Element is properly reflected */ - Reflect::require(); + /* declared above before + * class Reflect { .. } + */ + template + std::unique_ptr + EstablishTdx>::make() { + /* need to ensure Element is properly reflected */ + Reflect::require(); - return StdArrayTdx::make(); - } /*make*/ + return StdArrayTdx::make(); + } /*make*/ - // ----- std::vector ----- + // ----- std::vector ----- - /* declared above before - * class Reflect { .. } - */ - template - std::unique_ptr - EstablishTdx>::make() { - /* need to ensure Element is properly reflected */ - Reflect::require(); + /* declared above before + * class Reflect { .. } + */ + template + std::unique_ptr + EstablishTdx>::make() { + /* need to ensure Element is properly reflected */ + Reflect::require(); - return StdVectorTdx::make(); - } /*make*/ + return StdVectorTdx::make(); + } /*make*/ - // ----- std::pair ----- + // ----- std::pair ----- - /* declared above before - * class Reflect { .. } - */ - template - std::unique_ptr - EstablishTdx>::make() { - /* need to ensure Lhs, Rhs are properly reflected */ - Reflect::require(); - Reflect::require(); + /* declared above before + * class Reflect { .. } + */ + template + std::unique_ptr + EstablishTdx>::make() { + /* need to ensure Lhs, Rhs are properly reflected */ + Reflect::require(); + Reflect::require(); - return StructTdx::pair(); - } /*make*/ - } /*namespace reflect*/ + return StructTdx::pair(); + } /*make*/ + } /*namespace reflect*/ } /*namespace xo*/ /* end Reflect.hpp */ diff --git a/include/xo/reflect/TypeDescr.hpp b/include/xo/reflect/TypeDescr.hpp index da59c007..142a3287 100644 --- a/include/xo/reflect/TypeDescr.hpp +++ b/include/xo/reflect/TypeDescr.hpp @@ -2,7 +2,6 @@ #pragma once -//#include "reflect/atomic/AtomicTdx.hpp" #include "TypeDescrExtra.hpp" #include "cxxutil/demangle.hpp" #include @@ -16,80 +15,80 @@ #include namespace xo { - namespace reflect { - class TaggedPtr; /* see [reflect/TaggedPtr.hpp] */ + 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++); } + /* 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_; } + std::uint32_t id() const { return id_; } - private: - explicit TypeId(std::uint32_t id) : id_{id} {} + private: + explicit TypeId(std::uint32_t id) : id_{id} {} - private: - static std::uint32_t s_next_id; + private: + static std::uint32_t s_next_id; - /* unique index# for this type. - * 0 reserved for sentinel - */ - std::uint32_t id_ = 0; - }; /*TypeId*/ + /* 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<<*/ + 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; + /* runtime description of a struct/class instance variable */ + class StructMember; - class TypeDescrBase; + class TypeDescrBase; - using TypeDescr = TypeDescrBase const *; - using TypeDescrW = 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; + /* 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(); - */ - template - TypeInfoRef make() { return TypeInfoRef(&typeid(T)); } + /* use: + * TypeInfoRef tinfo = TypeInfoRef::make(); + */ + template + 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(); } + 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; + 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*/ + return ::strcmp(x.impl_name(), y.impl_name()) == 0; + } /*is_equal*/ - private: - /* native type_info object for encapsulated type */ - std::type_info const * tinfo_ = nullptr; - }; /*TypeInfoRef*/ - } /*namespace reflect*/ + private: + /* native type_info object for encapsulated type */ + std::type_info const * tinfo_ = nullptr; + }; /*TypeInfoRef*/ + } /*namespace reflect*/ } /*namespace xo*/ namespace std { From 61ddb8140e4a79f728a51bec9589d85b08328031 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 6 Oct 2023 18:02:22 -0400 Subject: [PATCH 056/111] refcnt: update after inserting xo into include path --- include/xo/reflect/Reflect.hpp | 2 +- include/xo/reflect/SelfTagging.hpp | 2 +- include/xo/reflect/TaggedRcptr.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/xo/reflect/Reflect.hpp b/include/xo/reflect/Reflect.hpp index 478078ad..89a75374 100644 --- a/include/xo/reflect/Reflect.hpp +++ b/include/xo/reflect/Reflect.hpp @@ -11,7 +11,7 @@ #include "pointer/PointerTdx.hpp" #include "vector/VectorTdx.hpp" #include "struct/StructTdx.hpp" -#include "refcnt/Refcounted.hpp" +#include "xo/refcnt/Refcounted.hpp" #include #include #include // for std::pair<> diff --git a/include/xo/reflect/SelfTagging.hpp b/include/xo/reflect/SelfTagging.hpp index e188b5c2..b41d4ce3 100644 --- a/include/xo/reflect/SelfTagging.hpp +++ b/include/xo/reflect/SelfTagging.hpp @@ -5,7 +5,7 @@ #pragma once -#include "Refcounted.hpp" +#include "xo/refcnt/Refcounted.hpp" #include "TypeDescr.hpp" #include "TaggedRcptr.hpp" diff --git a/include/xo/reflect/TaggedRcptr.hpp b/include/xo/reflect/TaggedRcptr.hpp index e31cffb3..3e06af64 100644 --- a/include/xo/reflect/TaggedRcptr.hpp +++ b/include/xo/reflect/TaggedRcptr.hpp @@ -8,7 +8,7 @@ #include "TaggedPtr.hpp" // causes #include cycle, reflect/Reflect.hpp includes this header //#include "reflect/Reflect.hpp" -#include "refcnt/Refcounted.hpp" +#include "xo/refcnt/Refcounted.hpp" namespace xo { namespace reflect { From f5c60e299b6743eae84342da41184435583d5da3 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 6 Oct 2023 18:09:41 -0400 Subject: [PATCH 057/111] xo-reflect: update subsys include location --- include/xo/reflect/init_reflect.hpp | 2 +- src/reflect/init_reflect.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/xo/reflect/init_reflect.hpp b/include/xo/reflect/init_reflect.hpp index 00506a25..b6365632 100644 --- a/include/xo/reflect/init_reflect.hpp +++ b/include/xo/reflect/init_reflect.hpp @@ -5,7 +5,7 @@ #pragma once -#include "subsys/Subsystem.hpp" +#include "xo/subsys/Subsystem.hpp" namespace xo { /* tag to represent the reflect/ subsystem within ordered initialization */ diff --git a/src/reflect/init_reflect.cpp b/src/reflect/init_reflect.cpp index 20dd1441..d95bf2e3 100644 --- a/src/reflect/init_reflect.cpp +++ b/src/reflect/init_reflect.cpp @@ -4,7 +4,7 @@ */ #include "init_reflect.hpp" -#include "subsys/Subsystem.hpp" +#include "xo/subsys/Subsystem.hpp" namespace xo { void From 1b3be7e7335d8c5d607656f75cd5a026963831d1 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 6 Oct 2023 18:43:49 -0400 Subject: [PATCH 058/111] build: account for xo/ insertion into include path --- include/xo/reflect/TypeDescr.hpp | 2 +- include/xo/reflect/pointer/PointerTdx.hpp | 2 +- src/reflect/TaggedRcptr.cpp | 2 +- src/reflect/TypeDescr.cpp | 2 +- src/reflect/struct/StructMember.cpp | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/xo/reflect/TypeDescr.hpp b/include/xo/reflect/TypeDescr.hpp index 142a3287..d908a77b 100644 --- a/include/xo/reflect/TypeDescr.hpp +++ b/include/xo/reflect/TypeDescr.hpp @@ -3,7 +3,7 @@ #pragma once #include "TypeDescrExtra.hpp" -#include "cxxutil/demangle.hpp" +#include "xo/cxxutil/demangle.hpp" #include #include #include diff --git a/include/xo/reflect/pointer/PointerTdx.hpp b/include/xo/reflect/pointer/PointerTdx.hpp index 327456cf..037d3ee4 100644 --- a/include/xo/reflect/pointer/PointerTdx.hpp +++ b/include/xo/reflect/pointer/PointerTdx.hpp @@ -7,7 +7,7 @@ #include "xo/reflect/TypeDescrExtra.hpp" #include "xo/reflect/EstablishTypeDescr.hpp" -#include "indentlog/scope.hpp" +#include "xo/indentlog/scope.hpp" namespace xo { namespace reflect { diff --git a/src/reflect/TaggedRcptr.cpp b/src/reflect/TaggedRcptr.cpp index cc461e0c..8d68a60b 100644 --- a/src/reflect/TaggedRcptr.cpp +++ b/src/reflect/TaggedRcptr.cpp @@ -4,7 +4,7 @@ */ #include "TaggedRcptr.hpp" -#include "indentlog/print/tag.hpp" +#include "xo/indentlog/print/tag.hpp" namespace xo { using xo::xtag; diff --git a/src/reflect/TypeDescr.cpp b/src/reflect/TypeDescr.cpp index 564fec14..93a0bfc1 100644 --- a/src/reflect/TypeDescr.cpp +++ b/src/reflect/TypeDescr.cpp @@ -4,7 +4,7 @@ #include "TaggedPtr.hpp" #include "TypeDescrExtra.hpp" #include "atomic/AtomicTdx.hpp" -#include "indentlog/scope.hpp" +#include "xo/indentlog/scope.hpp" namespace xo { using xo::scope; diff --git a/src/reflect/struct/StructMember.cpp b/src/reflect/struct/StructMember.cpp index 59d69056..7f27af8c 100644 --- a/src/reflect/struct/StructMember.cpp +++ b/src/reflect/struct/StructMember.cpp @@ -4,7 +4,7 @@ */ #include "struct/StructMember.hpp" -#include "indentlog/scope.hpp" +#include "xo/indentlog/scope.hpp" #include namespace xo { From 7d1e2f39029b400cafa91a1eda62ce9525eb877a Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 6 Oct 2023 23:54:42 -0400 Subject: [PATCH 059/111] drop cmake-config-mediated indentlog dep (will see if need later) --- cmake/reflectConfig.cmake.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/reflectConfig.cmake.in b/cmake/reflectConfig.cmake.in index 9c15f36a..e98de9c1 100644 --- a/cmake/reflectConfig.cmake.in +++ b/cmake/reflectConfig.cmake.in @@ -1,4 +1,6 @@ @PACKAGE_INIT@ +#include(CMakeFindDependencyMacro) +#find_dependency(indentlog) include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") check_required_components("@PROJECT_NAME@") From 0c73b5417b908697629be986ba45513c1e832646 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 6 Oct 2023 23:55:13 -0400 Subject: [PATCH 060/111] reflect: tidy indentlog dep --- src/reflect/CMakeLists.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/reflect/CMakeLists.txt b/src/reflect/CMakeLists.txt index 6056ebca..f8d84446 100644 --- a/src/reflect/CMakeLists.txt +++ b/src/reflect/CMakeLists.txt @@ -6,10 +6,11 @@ set(SELF_SOURCE_FILES TypeDescr.cpp TypeDescrExtra.cpp TaggedRcptr.cpp atomic/At xo_add_shared_library(${SELF_LIBRARY_NAME} ${PROJECT_VERSION} 1 ${SELF_SOURCE_FILES}) # ---------------------------------------------------------------- -# dependencies: logutil, ... +# dependencies: indentlog, ... -xo_internal_dependency(${SELF_LIBRARY_NAME} indentlog) -xo_internal_dependency(${SELF_LIBRARY_NAME} refcnt) +# note: changes here must coordinate with cmake/reflectConfig.cmake.in +xo_dependency_headeronly(${SELF_LIBRARY_NAME} indentlog) +xo_dependency(${SELF_LIBRARY_NAME} refcnt) # ---------------------------------------------------------------- # 3rd party dependency: boost: From 58b7f567e9a5bf772c317171bfd4b73972841ceb Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 8 Oct 2023 13:56:20 -0400 Subject: [PATCH 061/111] build: xo_dependency_headeronly() -> xo_dependency() --- src/reflect/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reflect/CMakeLists.txt b/src/reflect/CMakeLists.txt index f8d84446..e22a2c4c 100644 --- a/src/reflect/CMakeLists.txt +++ b/src/reflect/CMakeLists.txt @@ -9,7 +9,7 @@ xo_add_shared_library(${SELF_LIBRARY_NAME} ${PROJECT_VERSION} 1 ${SELF_SOURCE_FI # dependencies: indentlog, ... # note: changes here must coordinate with cmake/reflectConfig.cmake.in -xo_dependency_headeronly(${SELF_LIBRARY_NAME} indentlog) +xo_dependency(${SELF_LIBRARY_NAME} indentlog) xo_dependency(${SELF_LIBRARY_NAME} refcnt) # ---------------------------------------------------------------- From 26dffce47026e740564cca6ac01ff23f23b6c3a2 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 9 Oct 2023 14:00:54 -0400 Subject: [PATCH 062/111] build: bugfix: support transitive deps in find_package helper --- cmake/reflectConfig.cmake.in | 6 ++++-- src/reflect/CMakeLists.txt | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cmake/reflectConfig.cmake.in b/cmake/reflectConfig.cmake.in index e98de9c1..ce449a35 100644 --- a/cmake/reflectConfig.cmake.in +++ b/cmake/reflectConfig.cmake.in @@ -1,6 +1,8 @@ @PACKAGE_INIT@ -#include(CMakeFindDependencyMacro) -#find_dependency(indentlog) +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@") diff --git a/src/reflect/CMakeLists.txt b/src/reflect/CMakeLists.txt index e22a2c4c..27485780 100644 --- a/src/reflect/CMakeLists.txt +++ b/src/reflect/CMakeLists.txt @@ -8,9 +8,9 @@ xo_add_shared_library(${SELF_LIBRARY_NAME} ${PROJECT_VERSION} 1 ${SELF_SOURCE_FI # ---------------------------------------------------------------- # dependencies: indentlog, ... -# note: changes here must coordinate with cmake/reflectConfig.cmake.in -xo_dependency(${SELF_LIBRARY_NAME} indentlog) xo_dependency(${SELF_LIBRARY_NAME} refcnt) +xo_dependency(${SELF_LIBRARY_NAME} indentlog) +xo_dependency(${SELF_LIBRARY_NAME} subsys) # ---------------------------------------------------------------- # 3rd party dependency: boost: From d0ccff195f49712ded3bc015b6619ba8ef685f2d Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 9 Oct 2023 14:03:10 -0400 Subject: [PATCH 063/111] bugfix: include path + reindent --- include/xo/reflect/TypeDrivenMap.hpp | 56 ++++++++++++++-------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/include/xo/reflect/TypeDrivenMap.hpp b/include/xo/reflect/TypeDrivenMap.hpp index c38c8739..3eb42660 100644 --- a/include/xo/reflect/TypeDrivenMap.hpp +++ b/include/xo/reflect/TypeDrivenMap.hpp @@ -5,42 +5,42 @@ #pragma once -#include "reflect/TypeDescr.hpp" +#include "TypeDescr.hpp" #include namespace xo { - namespace reflect { - /* represents a map :: TypeId -> Value */ - template - class TypeDrivenMap { - public: - Value const * lookup(TypeId id) const { return this->lookup_slot(id); } + namespace reflect { + /* represents a map :: TypeId -> Value */ + template + 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; + Value * require(TypeId id) { return this->require_slot(id); } + Value * require(TypeDescr td) { return this->require_slot(td->id()); } - return &(this->contents_v_[id.id()]); - } /*lookup_slot*/ + private: + Value const * lookup_slot(TypeId id) const { + if (this->contents_v_.size() <= id.id()) + return nullptr; - 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()]); + } /*lookup_slot*/ - return &(this->contents_v_[id.id()]); - } /*require_slot*/ + Value * require_slot(TypeId id) { + if (this->contents_v_.size() <= id.id()) + this->contents_v_.resize(id.id() + 1); - private: - /* since TypeId/s are unique, compact sequence numbers, - * can efficiently store mapping to Values using a vector indexed by TypeId - */ - std::vector contents_v_; - }; /*TypeDrivenMap*/ - } /*namespace reflect*/ + 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 contents_v_; + }; /*TypeDrivenMap*/ + } /*namespace reflect*/ } /*namespace xo*/ From ccb934e2b5fffd2d02d22ed7ce992310b3da3bd5 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 9 Oct 2023 16:53:37 -0400 Subject: [PATCH 064/111] cosmetic: indentation in init_reflect.hpp --- include/xo/reflect/init_reflect.hpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/xo/reflect/init_reflect.hpp b/include/xo/reflect/init_reflect.hpp index b6365632..5f69eadd 100644 --- a/include/xo/reflect/init_reflect.hpp +++ b/include/xo/reflect/init_reflect.hpp @@ -8,14 +8,14 @@ #include "xo/subsys/Subsystem.hpp" namespace xo { - /* tag to represent the reflect/ subsystem within ordered initialization */ - enum S_reflect_tag {}; + /* tag to represent the reflect/ subsystem within ordered initialization */ + enum S_reflect_tag {}; - template<> - struct InitSubsys { - static void init(); - static InitEvidence require(); - }; + template<> + struct InitSubsys { + static void init(); + static InitEvidence require(); + }; } /*namespace xo*/ From be6d36a5c80f564de7c6ea100a0a3329da55fa3c Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 12 Oct 2023 10:51:25 -0400 Subject: [PATCH 065/111] build: print CMAKE_MODULE_PATH / CMAKE_PREFIX_PATH --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 40c2c41d..6bd4ca7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,13 @@ -# reflect/CMakeLists.txt +# xo-reflect/CMakeLists.txt cmake_minimum_required(VERSION 3.10) project(reflect VERSION 0.1) enable_language(CXX) +message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") +message(STATUS "CMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}") + # common XO cmake macros (see proj/xo-cmake) include(xo_macros/xo_cxx) include(xo_macros/code-coverage) From 47f86347a4842d6a5e728d76372fcb489b98fee6 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 17 Oct 2023 14:52:09 -0400 Subject: [PATCH 066/111] doc updates --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 3c5d34be..40032c62 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,12 @@ # reflection library +## Getting Started + +### build + install dependencies + +- [github/Rconybea/xo-refcnt](https://github.com/Rconybea/xo-refcnt) +- [github/Rconybea/xo-subsys](https://github.com/Rconybea/xo-subsys) + ### build + install ``` $ cd reflect From 8231420c5e4dfe226f68a0d5c144d389c7cf46e2 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 17 Oct 2023 14:52:27 -0400 Subject: [PATCH 067/111] cosmetic: indenting --- include/xo/reflect/TypeDescr.hpp | 351 ++++++++++++++++--------------- 1 file changed, 180 insertions(+), 171 deletions(-) diff --git a/include/xo/reflect/TypeDescr.hpp b/include/xo/reflect/TypeDescr.hpp index d908a77b..94aa3aa3 100644 --- a/include/xo/reflect/TypeDescr.hpp +++ b/include/xo/reflect/TypeDescr.hpp @@ -98,204 +98,213 @@ namespace std { } /*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); } + 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*/ + 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*/ + class EqualTypeInfoRef { + public: + bool operator()(TypeInfoRef x, TypeInfoRef y) const noexcept { return TypeInfoRef::is_equal(x, y); } + }; /*EqualTypeInfoRef*/ + } /*namespace detail*/ #endif - class TypeDescrExtra; + 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; + /* run-time description for a native c++ type */ + class TypeDescrBase { + public: + /* type-description objects for a type T is unique, + * --> can always use its address + */ + TypeDescrBase(TypeDescrBase const & x) = delete; - /* test whether a type has been reflected. - * introducing this for unit testing - */ - static bool is_reflected(std::type_info const * tinfo) { - return (s_type_table_map.find(TypeInfoRef(tinfo)) - != s_type_table_map.end()); - } /*is_reflected*/ + /* test whether a type has been reflected. + * introducing this for unit testing + */ + static bool is_reflected(std::type_info const * tinfo) { + return (s_type_table_map.find(TypeInfoRef(tinfo)) + != s_type_table_map.end()); + } /*is_reflected*/ - /* NOTE: - * implementation here will be defeated if std::type_info - * objects violate ODR. This occurs with clang + 2-level namespaces, - * so important to linke with --flat_namespace defined. - * See FAQ - * [Build Issues|Q2 - dynamic_cast> fails] - */ - static TypeDescrW require(std::type_info const * tinfo, - std::string_view canonical_name, - std::unique_ptr tdextra); + /* 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> fails] + */ + static TypeDescrW require(std::type_info const * tinfo, + std::string_view canonical_name, + std::unique_ptr tdextra); - /* print table of reflected types to os */ - static void print_reflected_types(std::ostream & os); + /* print table of reflected types to os */ + static void print_reflected_types(std::ostream & os); - TypeId id() const { return id_; } - std::type_info const * typeinfo() const { return typeinfo_; } - std::string_view const & canonical_name() const { return canonical_name_; } - std::string_view const & short_name() const { return short_name_; } - bool complete_flag() const { return complete_flag_; } - TypeDescrExtra * tdextra() const { return tdextra_.get(); } - Metatype metatype() const { return tdextra_->metatype(); } + TypeId id() const { return id_; } + std::type_info const * typeinfo() const { return typeinfo_; } + std::string_view const & canonical_name() const { return canonical_name_; } + std::string_view const & short_name() const { return short_name_; } + bool complete_flag() const { return complete_flag_; } + TypeDescrExtra * tdextra() const { return tdextra_.get(); } + Metatype metatype() const { return tdextra_->metatype(); } - /* true iff the type represented by *this is the same as the type T. - * - * Warning: comparing typeinfo address can give false negatives. - * suspect this is caused by problems coalescing linker symbols - * in the clang toolchain. - */ - template - bool is_native() const { - return ((this->typeinfo() == &typeid(T)) - || (this->typeinfo()->hash_code() == typeid(T).hash_code()) - || (this->typeinfo()->name() == typeid(T).name())); - } /*is_native*/ + /* true iff the type represented by *this is the same as the type T. + * + * Warning: comparing typeinfo address can give false negatives. + * suspect this is caused by problems coalescing linker symbols + * in the clang toolchain. + */ + template + bool is_native() const { + return ((this->typeinfo() == &typeid(T)) + || (this->typeinfo()->hash_code() == typeid(T).hash_code()) + || (this->typeinfo()->name() == typeid(T).name())); + } /*is_native*/ - /* safe downcast -- like dynamic_cast<>, but does not require a source type */ - template - T * recover_native(void * address) const { - if (this->is_native()) { - return reinterpret_cast(address); - } else { - return nullptr; - } - } /*recover_native*/ + /* safe downcast -- like dynamic_cast<>, but does not require a source type */ + template + T * recover_native(void * address) const { + if (this->is_native()) { + return reinterpret_cast(address); + } else { + return nullptr; + } + } /*recover_native*/ - bool is_vector() const { return this->tdextra_->is_vector(); } - bool is_struct() const { return this->tdextra_->is_struct(); } + bool is_vector() const { return this->tdextra_->is_vector(); } + bool is_struct() const { return this->tdextra_->is_struct(); } - /* given a T-instance object, return tagged pointer with T replaced - * by the most-derived-subtype of T to which *object belongs. - * This works only for descendants of reflect::SelfTagging - */ - TaggedPtr most_derived_self_tp(void * object) const; + /* 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, std::array, ..): - * .n_child() reports #of elements - * if struct/class: - * .n_child() reports #of instance variables (that have been reflected) - */ - uint32_t n_child(void * object) const { return this->tdextra_->n_child(object); } - TaggedPtr child_tp(uint32_t i, void * object) const; + /* if generalized vector (std::vector, std::array, ..): + * .n_child() reports #of elements + * if struct/class: + * .n_child() reports #of instance variables (that have been reflected) + */ + uint32_t n_child(void * object) const { return this->tdextra_->n_child(object); } + TaggedPtr child_tp(uint32_t i, void * object) const; - /* require: - * - .is_struct() = true - * - i in [0 .. .n_child() - 1] - */ - std::string const & struct_member_name(uint32_t i) const { - return this->tdextra_->struct_member_name(i); - } - /* fetch runtime description for i'th reflected instance variable. - * - * require: - * - .is_struct() = true - * - i in [0 .. .n_child() - 1] - */ - StructMember const & struct_member(uint32_t i) const { - StructMember const * sm = this->tdextra_->struct_member(i); + /* 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*/ + assert(sm); + return *sm; + } /*struct_member*/ - void display(std::ostream & os) const; - std::string display_string() const; + 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(); + /* mark this TypeDescr complete; + * returns the value of .complete_flag from _before_ + * this call + */ + bool mark_complete(); - /* call this once to attach extended type information to a type-description - * (e.g. description of struct members for a record type) - */ - void assign_tdextra(std::unique_ptr tdx); + /* call this once to attach extended type information to a type-description + * (e.g. description of struct members for a record type) + */ + void assign_tdextra(std::unique_ptr tdx); - private: - TypeDescrBase(TypeId id, - std::type_info const * tinfo, - std::string_view canonical_name, - std::unique_ptr tdextra); + private: + TypeDescrBase(TypeId id, + std::type_info const * tinfo, + std::string_view canonical_name, + std::unique_ptr tdextra); - private: - /* invariant: - * - for all TypeDescrImpl instances x: - * - s_type_table_v[x->id()] = x - * - s_type_table_map[TypeInfoRef(x->typeinfo())] = x - */ + private: + /* invariant: + * - for all TypeDescrImpl instances x: + * - s_type_table_v[x->id()] = x + * - s_type_table_map[TypeInfoRef(x->typeinfo())] = x + */ - /* hashmap of all TypeDescr instances, indexed by . singleton */ - static std::unordered_map> s_type_table_map; - /* hashmap of (presumed) duplicate TypeInfoRef values. - * This happens with clang sometimes when the same type is referenced - * from multiple modules (i.e. shared libs). - */ - static std::unordered_map s_coalesced_type_table_map; + /* hashmap of all TypeDescr instances, indexed by . singleton */ + static std::unordered_map> s_type_table_map; + /* hashmap of (presumed) duplicate TypeInfoRef values. + * This happens with clang sometimes when the same type is referenced + * from multiple modules (i.e. shared libs). + */ + static std::unordered_map s_coalesced_type_table_map; - /* vector of all TypeDescr instances. singleton. */ - static std::vector s_type_table_v; + /* vector of all TypeDescr instances. singleton. */ + static std::vector s_type_table_v; - private: - /* unique id# for this type */ - TypeId id_; - /* typeinfo for type T */ - std::type_info const * typeinfo_ = nullptr; - /* canonical name for this type (see demangle.hpp for type_name()) - * e.g. - * xo::option::Px2 - */ - std::string_view canonical_name_; - /* suffix of .canonical_name, just after last ':' - * e.g. - * Px2 - */ - std::string_view short_name_; - /* set to true once final value for .tdextra is established - * intially all TypeDescr objects will use AtomicTdx for .tdextra - * Reflect::require() upgrades .tdextra for particular types. - * When that procedure makes a decision for a type T, - * .complete_flag will be set to true for the corresponding TypeDescrBase instance - */ - bool complete_flag_ = false; - /* additional type information that either: - * (a) isn't universal across all types, - * e.g. dereferencing instance of a pointer type - * (b) can't be captured with template-fu, - * e.g. struct member names - * - * generally .tdextra will be populated some time after TypeDescrBase's ctor exits. - * This is necessary because of (b) above, also because of possibility of recursive - * types. - */ - std::unique_ptr tdextra_; - }; /*TypeDescrBase*/ + private: + /* unique id# for this type */ + TypeId id_; + /* typeinfo for type T */ + std::type_info const * typeinfo_ = nullptr; + /* canonical name for this type (see demangle.hpp for type_name()) + * e.g. + * xo::option::Px2 + */ + std::string_view canonical_name_; + /* suffix of .canonical_name, just after last ':' + * e.g. + * Px2 + */ + std::string_view short_name_; + /* set to true once final value for .tdextra is established + * intially all TypeDescr objects will use AtomicTdx for .tdextra + * Reflect::require() upgrades .tdextra for particular types. + * When that procedure makes a decision for a type T, + * .complete_flag will be set to true for the corresponding TypeDescrBase instance + */ + bool complete_flag_ = false; + /* additional type information that either: + * (a) isn't universal across all types, + * e.g. dereferencing instance of a pointer type + * (b) can't be captured with template-fu, + * e.g. struct member names + * + * generally .tdextra will be populated some time after TypeDescrBase's ctor exits. + * This is necessary because of (b) above, also because of possibility of recursive + * types. + */ + std::unique_ptr tdextra_; + }; /*TypeDescrBase*/ - inline std::ostream & - operator<<(std::ostream & os, TypeDescrBase const & x) { - x.display(os); - return os; - } /*operator<<*/ + inline std::ostream & + operator<<(std::ostream & os, TypeDescrBase const & x) { + x.display(os); + return os; + } /*operator<<*/ - } /*namespace reflect*/ + /* 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; + } + + } /*namespace reflect*/ } /*namespace xo*/ /* end TypeDescr.hpp */ From 0707571945155eb9e05e51426d3c96a83eea99c6 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 17 Oct 2023 14:52:44 -0400 Subject: [PATCH 068/111] cosmetic: indentation --- src/reflect/TypeDescr.cpp | 300 +++++++++++++++++++------------------- 1 file changed, 150 insertions(+), 150 deletions(-) diff --git a/src/reflect/TypeDescr.cpp b/src/reflect/TypeDescr.cpp index 93a0bfc1..b0a0dfd7 100644 --- a/src/reflect/TypeDescr.cpp +++ b/src/reflect/TypeDescr.cpp @@ -7,188 +7,188 @@ #include "xo/indentlog/scope.hpp" namespace xo { - using xo::scope; - using xo::xtag; - using xo::tostr; + using xo::scope; + using xo::xtag; + using xo::tostr; - namespace reflect { - uint32_t - TypeId::s_next_id = 1; + namespace reflect { + uint32_t + TypeId::s_next_id = 1; - std::unordered_map> - TypeDescrBase::s_type_table_map; + std::unordered_map> + TypeDescrBase::s_type_table_map; - std::unordered_map - TypeDescrBase::s_coalesced_type_table_map; + std::unordered_map + TypeDescrBase::s_coalesced_type_table_map; - std::vector - TypeDescrBase::s_type_table_v; + std::vector + TypeDescrBase::s_type_table_v; - TypeDescrW - TypeDescrBase::require(std::type_info const * tinfo, - std::string_view canonical_name, - std::unique_ptr tdextra) - { - /* 1. lookup by tinfo hash_code in s_type_table_map */ - { - auto ix = s_type_table_map.find(TypeInfoRef(tinfo)); + TypeDescrW + TypeDescrBase::require(std::type_info const * tinfo, + std::string_view canonical_name, + std::unique_ptr tdextra) + { + /* 1. lookup by tinfo hash_code in s_type_table_map */ + { + auto ix = s_type_table_map.find(TypeInfoRef(tinfo)); - if ((ix != s_type_table_map.end()) && ix->second) - return ix->second.get(); - } + if ((ix != s_type_table_map.end()) && ix->second) + return ix->second.get(); + } - /* 2. lookup by tinfo hash_code in s_coalesced_type_table_map */ - { - auto ix = s_coalesced_type_table_map.find(TypeInfoRef(tinfo)); + /* 2. lookup by tinfo hash_code in s_coalesced_type_table_map */ + { + auto ix = s_coalesced_type_table_map.find(TypeInfoRef(tinfo)); - if ((ix != s_coalesced_type_table_map.end()) && ix->second) - return ix->second; - } + if ((ix != s_coalesced_type_table_map.end()) && ix->second) + return ix->second; + } - /* 3. O(n) lookup by canonical_name, before we create a new slot. - * - * Have to accept that on clang type_info objects aren't always unique (!$@#!!) - * - * TODO: lookup table keyed by canonical_name - */ - for (TypeDescrBase * x : s_type_table_v) { - if (x && (x->canonical_name() == canonical_name)) { - /* 1. assume *x represents the type associated with tinfo. - * 2. *do* store tinfo in s_coalesced_type_table_map[], - * for faster lookup next time - */ - s_coalesced_type_table_map[TypeInfoRef(tinfo)] = x; + /* 3. O(n) lookup by canonical_name, before we create a new slot. + * + * Have to accept that on clang type_info objects aren't always unique (!$@#!!) + * + * TODO: lookup table keyed by canonical_name + */ + for (TypeDescrBase * x : s_type_table_v) { + if (x && (x->canonical_name() == canonical_name)) { + /* 1. assume *x represents the type associated with tinfo. + * 2. *do* store tinfo in s_coalesced_type_table_map[], + * for faster lookup next time + */ + s_coalesced_type_table_map[TypeInfoRef(tinfo)] = x; - return x; - } - } + return x; + } + } - TypeId id = TypeId::allocate(); + TypeId id = TypeId::allocate(); - std::unique_ptr & slot = s_type_table_map[TypeInfoRef(tinfo)]; + std::unique_ptr & slot = s_type_table_map[TypeInfoRef(tinfo)]; - slot.reset(new TypeDescrBase(id, - tinfo, - canonical_name, - std::move(tdextra))); + slot.reset(new TypeDescrBase(id, + tinfo, + canonical_name, + std::move(tdextra))); - if (s_type_table_v.size() <= id.id()) - s_type_table_v.resize(id.id() + 1); + if (s_type_table_v.size() <= id.id()) + s_type_table_v.resize(id.id() + 1); - s_type_table_v[id.id()] = slot.get(); + s_type_table_v[id.id()] = slot.get(); - return slot.get(); - } /*require*/ + return slot.get(); + } /*require*/ - void - TypeDescrBase::print_reflected_types(std::ostream & os) - { - os << "display(os); - } - } + for (TypeDescrBase * td : s_type_table_v) { + os << "\n "; + if (td) { + td->display(os); + } + } - os << ">\n"; - } /*print_reflected_types*/ + os << ">\n"; + } /*print_reflected_types*/ - namespace { - /* readability hack: - * foo::bar::Quux ==> Quux - * but lookout for template names: - * std::pair ==> pair - */ - std::string_view - unqualified_name(std::string_view const & canonical_name) - { - size_t m = canonical_name.find_first_of('<'); + namespace { + /* readability hack: + * foo::bar::Quux ==> Quux + * but lookout for template names: + * std::pair ==> pair + */ + 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); + /* 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*/ + 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, - std::type_info const * tinfo, - std::string_view canonical_name, - std::unique_ptr tdextra) - : id_{std::move(id)}, - typeinfo_{tinfo}, - canonical_name_{std::move(canonical_name)}, - short_name_{unqualified_name(canonical_name_)}, - tdextra_{std::move(tdextra)} - { - } + TypeDescrBase::TypeDescrBase(TypeId id, + std::type_info const * tinfo, + std::string_view canonical_name, + std::unique_ptr tdextra) + : id_{std::move(id)}, + typeinfo_{tinfo}, + canonical_name_{std::move(canonical_name)}, + short_name_{unqualified_name(canonical_name_)}, + 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::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*/ + 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 << "metatype()) - << ">"; - } /*display*/ + void + TypeDescrBase::display(std::ostream & os) const + { + os << "metatype()) + << ">"; + } /*display*/ - std::string - TypeDescrBase::display_string() const - { - return tostr(*this); - } /*display_string*/ + std::string + TypeDescrBase::display_string() const + { + return tostr(*this); + } /*display_string*/ - bool - TypeDescrBase::mark_complete() - { - bool retval = this->complete_flag_; + bool + TypeDescrBase::mark_complete() + { + bool retval = this->complete_flag_; - this->complete_flag_ = true; + this->complete_flag_ = true; - return retval; - } /*mark_complete*/ + return retval; + } /*mark_complete*/ - void - TypeDescrBase::assign_tdextra(std::unique_ptr 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())); + void + TypeDescrBase::assign_tdextra(std::unique_ptr 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*/ - } /*namespace reflect*/ + this->complete_flag_ = true; + this->tdextra_ = std::move(tdx); + } /*assign_tdextra*/ + } /*namespace reflect*/ } /*namespace xo*/ /* end TypeDescr.cpp */ From 0d6e9afc0a757c0275591341b664632777714582 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 17 Oct 2023 14:52:53 -0400 Subject: [PATCH 069/111] build: tidy in CMakeLists.txt --- utest/CMakeLists.txt | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/utest/CMakeLists.txt b/utest/CMakeLists.txt index 8d142b9b..b701f2a6 100644 --- a/utest/CMakeLists.txt +++ b/utest/CMakeLists.txt @@ -9,22 +9,6 @@ xo_include_options2(${SELF_EXECUTABLE_NAME}) add_test(NAME ${SELF_EXECUTABLE_NAME} COMMAND ${SELF_EXECUTABLE_NAME}) target_code_coverage(${SELF_EXECUTABLE_NAME} AUTO ALL) -# ---------------------------------------------------------------- -# generic project dependency - -# PROJECT_SOURCE_DIR: -# so we can for example write -# #include "indentlog/scope.hpp" -# from anywhere in the project -# PROJECT_BINARY_DIR: -# since version file will be in build directory, need that directory -# to also be included in compiler's include path -# -#xo_include_options2(${SELF_EXECUTABLE_NAME}) -#target_include_directories(${SELF_EXECUTABLE_NAME} PUBLIC -# ${PROJECT_SOURCE_DIR} -# ${PROJECT_BINARY_DIR}) - # ---------------------------------------------------------------- # internal dependencies: logutil, ... @@ -35,15 +19,6 @@ xo_self_dependency(${SELF_EXECUTABLE_NAME} reflect) xo_external_target_dependency(${SELF_EXECUTABLE_NAME} Catch2 Catch2::Catch2) -# need this so that catch2/include appears in compile_commands.json, -# on which lsp integration relies. -# -# See also /nix/store/*-catch2-*/lib/cmake/Catch2/ParseAndAddCatchTests.cmake; -# commands here derived from ^ .cmake file -# -#find_path(CATCH_INCLUDE_DIR "catch2/catch.hpp") -#target_include_directories(${SELF_EXECUTABLE_NAME} PUBLIC ${CATCH_INCLUDE_DIR}) - # ---------------------------------------------------------------- # make standard directories for std:: includes explicit # so that From ea36e50ea07a256dbadeeb243b1654793e69520b Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 19 Oct 2023 16:26:27 -0400 Subject: [PATCH 070/111] minor tweaks : doc , build messaging --- CMakeLists.txt | 6 +++--- README.md | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6bd4ca7a..e7c1b6eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,8 +5,8 @@ cmake_minimum_required(VERSION 3.10) project(reflect VERSION 0.1) enable_language(CXX) -message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") -message(STATUS "CMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}") +#message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") +#message(STATUS "CMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}") # common XO cmake macros (see proj/xo-cmake) include(xo_macros/xo_cxx) @@ -50,4 +50,4 @@ xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets # ---------------------------------------------------------------- # install .hpp files -xo_install_include_tree() +#xo_install_include_tree() diff --git a/README.md b/README.md index 40032c62..ecbafe0c 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ ### build + install dependencies -- [github/Rconybea/xo-refcnt](https://github.com/Rconybea/xo-refcnt) -- [github/Rconybea/xo-subsys](https://github.com/Rconybea/xo-subsys) +- [github/Rconybea/refcnt](https://github.com/Rconybea/refcnt) +- [github/Rconybea/subsys](https://github.com/Rconybea/subsys) ### build + install ``` From ee6eb49354e5769cd98dbb1308da0cb0e307e133 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 19 Oct 2023 16:26:49 -0400 Subject: [PATCH 071/111] build: make symlink-aware --- src/reflect/CMakeLists.txt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/reflect/CMakeLists.txt b/src/reflect/CMakeLists.txt index 27485780..00d0b520 100644 --- a/src/reflect/CMakeLists.txt +++ b/src/reflect/CMakeLists.txt @@ -1,20 +1,21 @@ # reflect/CMakeLists.txt -set(SELF_LIBRARY_NAME reflect) -set(SELF_SOURCE_FILES 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(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 init_reflect.cpp) -xo_add_shared_library(${SELF_LIBRARY_NAME} ${PROJECT_VERSION} 1 ${SELF_SOURCE_FILES}) +#xo_add_shared_library(${SELF_LIB} ${PROJECT_VERSION} 1 ${SELF_SRCS}) +xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}) # ---------------------------------------------------------------- # dependencies: indentlog, ... -xo_dependency(${SELF_LIBRARY_NAME} refcnt) -xo_dependency(${SELF_LIBRARY_NAME} indentlog) -xo_dependency(${SELF_LIBRARY_NAME} subsys) +xo_dependency(${SELF_LIB} refcnt) +xo_dependency(${SELF_LIB} indentlog) +xo_dependency(${SELF_LIB} subsys) # ---------------------------------------------------------------- # 3rd party dependency: boost: -#xo_boost_dependency(${SELF_LIBRARY_NAME}) +#xo_boost_dependency(${SELF_LIB}) # end CMakeLists.txt From 7b433e3f4ec0a162ac8bca7058b9b68156f3c9fc Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 22 Oct 2023 14:52:39 -0400 Subject: [PATCH 072/111] build: .cmake code layout --- src/reflect/CMakeLists.txt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/reflect/CMakeLists.txt b/src/reflect/CMakeLists.txt index 00d0b520..397876fa 100644 --- a/src/reflect/CMakeLists.txt +++ b/src/reflect/CMakeLists.txt @@ -1,9 +1,14 @@ # 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 init_reflect.cpp) +set(SELF_SRCS + TypeDescr.cpp TypeDescrExtra.cpp TaggedRcptr.cpp + atomic/AtomicTdx.cpp + pointer/PointerTdx.cpp + vector/VectorTdx.cpp + struct/StructTdx.cpp struct/StructMember.cpp + init_reflect.cpp) -#xo_add_shared_library(${SELF_LIB} ${PROJECT_VERSION} 1 ${SELF_SRCS}) xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}) # ---------------------------------------------------------------- From b0b3e7c0c8b297fad95daefa4ad197f1828e9592 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 13 Mar 2024 15:30:00 -0400 Subject: [PATCH 073/111] cosmetic: layout, copypasta comments --- utest/StructTdx.test.cpp | 76 +++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/utest/StructTdx.test.cpp b/utest/StructTdx.test.cpp index cb845e18..6b1fb2cd 100644 --- a/utest/StructTdx.test.cpp +++ b/utest/StructTdx.test.cpp @@ -7,53 +7,51 @@ #include namespace xo { - using xo::reflect::Reflect; - using xo::reflect::TaggedPtr; - using xo::reflect::TypeDescr; - using xo::reflect::Metatype; + 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 p; + namespace ut { + TEST_CASE("std-pair-reflect", "[reflect]") { + std::pair p; - TaggedPtr tp = Reflect::make_tp(&p); - //TypeDescr td = Reflect::require>(); + TaggedPtr tp = Reflect::make_tp(&p); - REQUIRE(Reflect::is_reflected>() == true); + REQUIRE(Reflect::is_reflected>() == 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>() == &p); - REQUIRE(tp.n_child() == 2); /* struct with 2 members */ - REQUIRE(tp.struct_member_name(0) == "first"); - REQUIRE(tp.struct_member_name(1) == "second"); + 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>() == &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); + 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() == &(p.first)); - REQUIRE(tp0.n_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() == &(p.first)); + REQUIRE(tp0.n_child() == 0); - TaggedPtr tp1 = tp.get_child(1); + 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() == &(p.second)); - REQUIRE(tp1.n_child() == 0); + 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() == &(p.second)); + REQUIRE(tp1.n_child() == 0); - } /*TEST_CASE(std-pair-reflect)*/ - - } /*namespace ut*/ + } /*TEST_CASE(std-pair-reflect)*/ + } /*namespace ut*/ } /*namespace xo*/ -/* end VectorTdx.test.cpp */ +/* end StructTdx.test.cpp */ From fe986a320a2b1993a8399602cb3a3b6dc8c86253 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 15 Mar 2024 19:26:43 -0400 Subject: [PATCH 074/111] build: streamline CMAKE_MODULE_PATH interaction --- .gitignore | 2 +- CMakeLists.txt | 3 +-- cmake/xo-bootstrap-macros.cmake | 12 ++++++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 cmake/xo-bootstrap-macros.cmake diff --git a/.gitignore b/.gitignore index eff45bd2..13c0afb7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # clangd working space (see emacs+lsp) .cache # typical cmake build directory (source-tree-nephew) -build*/* +.build* # symlink to builddir/compile_commands.json; should be set manually in dev sandbox compile_commands.json diff --git a/CMakeLists.txt b/CMakeLists.txt index e7c1b6eb..de455b56 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,8 +9,7 @@ enable_language(CXX) #message(STATUS "CMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}") # common XO cmake macros (see proj/xo-cmake) -include(xo_macros/xo_cxx) -include(xo_macros/code-coverage) +include(cmake/xo-bootstrap-macros.cmake) # ---------------------------------------------------------------- # unit test setup diff --git a/cmake/xo-bootstrap-macros.cmake b/cmake/xo-bootstrap-macros.cmake new file mode 100644 index 00000000..16644435 --- /dev/null +++ b/cmake/xo-bootstrap-macros.cmake @@ -0,0 +1,12 @@ +if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL "prefix")) + # default to typical install location for xo-project-macros + set(CMAKE_MODULE_PATH ${CMAKE_INSTALL_PREFIX}/share/cmake) +endif() + +message("-- CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}") +message("-- CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") + +# 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-project-macros) From c71bcb6415ff91cb777f6875c1574e3b277b6b6f Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 1 May 2024 14:33:57 -0400 Subject: [PATCH 075/111] xo-reflect: build: streamline using recent xo-cmake improvements --- CMakeLists.txt | 35 +++++++++------------------------ cmake/xo-bootstrap-macros.cmake | 35 +++++++++++++++++++++++++++------ utest/CMakeLists.txt | 23 ++-------------------- 3 files changed, 40 insertions(+), 53 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index de455b56..050bc94c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,28 +3,19 @@ cmake_minimum_required(VERSION 3.10) project(reflect VERSION 0.1) -enable_language(CXX) -#message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") -#message(STATUS "CMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}") - -# common XO cmake macros (see proj/xo-cmake) +include(GNUInstallDirs) include(cmake/xo-bootstrap-macros.cmake) -# ---------------------------------------------------------------- -# unit test setup +xo_cxx_toplevel_options2() -enable_testing() -# activate code coverage for all executables + libraries (when configured with -DCODE_COVERAGE=ON) -add_code_coverage() -# 1. assuming that /nix/store/ prefixes .hpp files belonging to gcc, catch2 etc. -# we're not interested in code coverage for these sources. -# 2. exclude the utest/ subdir, we don't need coverage on the unit tests themselves; -# rather, want coverage on the code that the unit tests exercise. -# -# NOTE: this seems to work only with the 'ccov-all' target. In particular, doesn't seem to do anything with the 'ccov' target -# -add_code_coverage_all_targets(EXCLUDE /nix/store/* ${PROJECT_SOURCE_DIR}/utest/* ${PROJECT_BINARY_DIR}/local/* ${PROJECT_SOURCE_DIR}/repo/*) +# ---------------------------------------------------------------- +# cmake -DCMAKE_BUILD_TYPE=coverage +xo_toplevel_coverage_config2() + +# ---------------------------------------------------------------- +# cmake -DCMAKE_BUILD_TYPE=debug +xo_toplevel_debug_config2() # ---------------------------------------------------------------- # c++ settings @@ -33,10 +24,7 @@ set(PROJECT_CXX_FLAGS "") #set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2") add_definitions(${PROJECT_CXX_FLAGS}) -xo_toplevel_compile_options() - # ---------------------------------------------------------------- -# sources add_subdirectory(src/reflect) add_subdirectory(utest) @@ -45,8 +33,3 @@ add_subdirectory(utest) # provide find_package() support xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) - -# ---------------------------------------------------------------- -# install .hpp files - -#xo_install_include_tree() diff --git a/cmake/xo-bootstrap-macros.cmake b/cmake/xo-bootstrap-macros.cmake index 16644435..aba31169 100644 --- a/cmake/xo-bootstrap-macros.cmake +++ b/cmake/xo-bootstrap-macros.cmake @@ -1,12 +1,35 @@ -if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL "prefix")) - # default to typical install location for xo-project-macros - set(CMAKE_MODULE_PATH ${CMAKE_INSTALL_PREFIX}/share/cmake) +# ---------------------------------------------------------------- +# 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("-- CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}") -message("-- CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") +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-project-macros) +include(xo_macros/xo_cxx) + +xo_cxx_bootstrap_message() diff --git a/utest/CMakeLists.txt b/utest/CMakeLists.txt index b701f2a6..aef60b2f 100644 --- a/utest/CMakeLists.txt +++ b/utest/CMakeLists.txt @@ -3,31 +3,12 @@ set(SELF_EXECUTABLE_NAME utest.reflect) set(SELF_SOURCE_FILES reflect_utest_main.cpp StructReflector.test.cpp VectorTdx.test.cpp StructTdx.test.cpp) -add_executable(${SELF_EXECUTABLE_NAME} ${SELF_SOURCE_FILES}) -xo_include_options2(${SELF_EXECUTABLE_NAME}) - -add_test(NAME ${SELF_EXECUTABLE_NAME} COMMAND ${SELF_EXECUTABLE_NAME}) -target_code_coverage(${SELF_EXECUTABLE_NAME} AUTO ALL) +xo_add_utest_executable(${SELF_EXECUTABLE_NAME} ${SELF_SOURCE_FILES}) # ---------------------------------------------------------------- -# internal dependencies: logutil, ... +# deps: indentlog, .. xo_self_dependency(${SELF_EXECUTABLE_NAME} reflect) - -# ---------------------------------------------------------------- -# 3rd party dependency: catch2: - xo_external_target_dependency(${SELF_EXECUTABLE_NAME} Catch2 Catch2::Catch2) -# ---------------------------------------------------------------- -# make standard directories for std:: includes explicit -# so that -# (1) they appear in compile_commands.json. -# (2) clangd (run from emacs lsp-mode) can find them -# -if(CMAKE_EXPORT_COMPILE_COMMANDS) - set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES - ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) -endif() - # end CMakeLists.txt From 9d31c6ad8776abbb162ba091837244ee1ca66ce2 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 14 Jun 2024 10:53:23 -0400 Subject: [PATCH 076/111] xo-reflect: cosmetic: code layout --- include/xo/reflect/vector/VectorTdx.hpp | 124 ++++++++++++------------ 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/include/xo/reflect/vector/VectorTdx.hpp b/include/xo/reflect/vector/VectorTdx.hpp index 944c451c..84e414b4 100644 --- a/include/xo/reflect/vector/VectorTdx.hpp +++ b/include/xo/reflect/vector/VectorTdx.hpp @@ -9,87 +9,87 @@ #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 make(); + 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 make(); - // ----- Inherited from TypeDescrExtra ----- + // ----- Inherited from TypeDescrExtra ----- - virtual Metatype metatype() const override { return Metatype::mt_vector; } - virtual uint32_t n_child(void * object) const override = 0; - virtual TaggedPtr child_tp(uint32_t i, void * object) const override = 0; - /* (forbidden) */ - virtual std::string const & struct_member_name(uint32_t i) const override; - }; /*VectorTdx*/ + virtual Metatype metatype() const override { return Metatype::mt_vector; } + virtual uint32_t n_child(void * object) const override = 0; + virtual TaggedPtr child_tp(uint32_t i, void * object) const override = 0; + /* (forbidden) */ + virtual std::string const & struct_member_name(uint32_t i) const override; + }; /*VectorTdx*/ - // ----- StlVectorTdx ----- + // ----- StlVectorTdx ----- - /* require: - * - VectorT.size() - * - VectorT[int] :: lvalue - */ - template - class StlVectorTdx : public VectorTdx { - public: - using target_t = VectorT; + /* require: + * - VectorT.size() + * - VectorT[int] :: lvalue + */ + template + class StlVectorTdx : public VectorTdx { + public: + using target_t = VectorT; - static std::unique_ptr make() { - return std::unique_ptr(new StlVectorTdx()); - } /*make*/ + static std::unique_ptr make() { + return std::unique_ptr(new StlVectorTdx()); + } /*make*/ - virtual uint32_t n_child(void * object) const override { - target_t * vec = reinterpret_cast(object); + virtual uint32_t n_child(void * object) const override { + target_t * vec = reinterpret_cast(object); - return vec->size(); - } /*n_child*/ + return vec->size(); + } /*n_child*/ - virtual TaggedPtr child_tp(uint32_t i, void * object) const override { - target_t * vec = reinterpret_cast(object); + virtual TaggedPtr child_tp(uint32_t i, void * object) const override { + target_t * vec = reinterpret_cast(object); - return establish_most_derived_tp(&((*vec)[i])); - } /*child_tp*/ - }; /*StlVectorTdx*/ + return establish_most_derived_tp(&((*vec)[i])); + } /*child_tp*/ + }; /*StlVectorTdx*/ - // ----- std::array ----- + // ----- std::array ----- - /* coordinates with EstablishTdx>::make(), - * see [reflect/Reflect.hpp] - */ + /* coordinates with EstablishTdx>::make(), + * see [reflect/Reflect.hpp] + */ - template - using StdArrayTdx = StlVectorTdx>; + template + using StdArrayTdx = StlVectorTdx>; - // ----- std::vector ----- + // ----- std::vector ----- - /* coordinates with EstablishTdx>::make() - * see [reflect/Reflect.hpp] - */ - template - class StdVectorTdx : public VectorTdx { - public: - using target_t = std::vector; + /* coordinates with EstablishTdx>::make() + * see [reflect/Reflect.hpp] + */ + template + class StdVectorTdx : public VectorTdx { + public: + using target_t = std::vector; - static std::unique_ptr make() { - return std::unique_ptr(new StdVectorTdx()); - } /*make*/ + static std::unique_ptr make() { + return std::unique_ptr(new StdVectorTdx()); + } /*make*/ - virtual uint32_t n_child(void * object) const override { - target_t * vec = reinterpret_cast(object); + virtual uint32_t n_child(void * object) const override { + target_t * vec = reinterpret_cast(object); - return vec->size(); - } /*n_child*/ + return vec->size(); + } /*n_child*/ - virtual TaggedPtr child_tp(uint32_t i, void * object) const override { - target_t * vec = reinterpret_cast(object); + virtual TaggedPtr child_tp(uint32_t i, void * object) const override { + target_t * vec = reinterpret_cast(object); - return establish_most_derived_tp(&((*vec)[i])); - } - }; /*StdVectorTdx*/ - } /*namespace reflect*/ + return establish_most_derived_tp(&((*vec)[i])); + } + }; /*StdVectorTdx*/ + } /*namespace reflect*/ } /*namespace xo*/ From 9db09697337595aaad6b006583dadf31e9a3ce44 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 14 Jun 2024 10:53:35 -0400 Subject: [PATCH 077/111] xo-reflect: cosmetic: code layout --- include/xo/reflect/struct/StructTdx.hpp | 136 ++++++++++++------------ 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/include/xo/reflect/struct/StructTdx.hpp b/include/xo/reflect/struct/StructTdx.hpp index 594de2d5..15eebe9b 100644 --- a/include/xo/reflect/struct/StructTdx.hpp +++ b/include/xo/reflect/struct/StructTdx.hpp @@ -11,85 +11,85 @@ #include 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 make(std::vector member_v, - bool have_to_self_tp, - std::function to_self_tp); + 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 make(std::vector member_v, + bool have_to_self_tp, + std::function to_self_tp); - /* specialization for std::pair - * coordinates with [reflect/Reflect.hpp] - */ - template - static std::unique_ptr pair() { - using struct_t = std::pair; + /* specialization for std::pair + * coordinates with [reflect/Reflect.hpp] + */ + template + static std::unique_ptr pair() { + using struct_t = std::pair; - std::vector mv; - { - auto lhs_access - (GeneralStructMemberAccessor::make - (&struct_t::first)); + std::vector mv; + { + auto lhs_access + (GeneralStructMemberAccessor::make + (&struct_t::first)); - mv.push_back(StructMember("first", std::move(lhs_access))); - } - { - auto rhs_access - (GeneralStructMemberAccessor::make - (&struct_t::second)); + mv.push_back(StructMember("first", std::move(lhs_access))); + } + { + auto rhs_access + (GeneralStructMemberAccessor::make + (&struct_t::second)); - mv.push_back(StructMember("second", std::move(rhs_access))); - } + mv.push_back(StructMember("second", std::move(rhs_access))); + } - std::function null_to_self_tp; + std::function null_to_self_tp; - return make(std::move(mv), - false /*!have_to_self_tp*/, - null_to_self_tp); - } /*pair*/ + return make(std::move(mv), + false /*!have_to_self_tp*/, + null_to_self_tp); + } /*pair*/ - // ----- Inherited from TypeDescrExtra ----- + // ----- Inherited from TypeDescrExtra ----- - virtual Metatype metatype() const override { return Metatype::mt_struct; } - virtual TaggedPtr most_derived_self_tp(TypeDescrBase const * object_td, - void * object) const override { - if (this->have_to_self_tp_) { - return this->to_self_tp_(object); - } else { - return TypeDescrExtra::most_derived_self_tp(object_td, object); - } - } - virtual uint32_t n_child(void * /*object*/) const override { return this->member_v_.size(); } - virtual TaggedPtr child_tp(uint32_t i, void * object) const override; - virtual std::string const & struct_member_name(uint32_t i) const override; - virtual StructMember const * struct_member(uint32_t i) const override; + virtual Metatype metatype() const override { return Metatype::mt_struct; } + virtual TaggedPtr most_derived_self_tp(TypeDescrBase const * object_td, + void * object) const override { + if (this->have_to_self_tp_) { + return this->to_self_tp_(object); + } else { + return TypeDescrExtra::most_derived_self_tp(object_td, object); + } + } + virtual uint32_t n_child(void * /*object*/) const override { return this->member_v_.size(); } + virtual TaggedPtr child_tp(uint32_t i, void * object) const override; + virtual std::string const & struct_member_name(uint32_t i) const override; + virtual StructMember const * struct_member(uint32_t i) const override; - private: - StructTdx(std::vector member_v, - bool have_to_self_tp, - std::function 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: + StructTdx(std::vector member_v, + bool have_to_self_tp, + std::function 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 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 to_self_tp_; - }; /*StructTdx*/ + private: + /* per-instance-variable reflection details */ + std::vector 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 to_self_tp_; + }; /*StructTdx*/ - } /*namespace reflect*/ + } /*namespace reflect*/ } /*namespace xo*/ /* end StructTdx.hpp */ From 0fe2b5107698d1b6d5dbe659977a0dbd6465f599 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 14 Jun 2024 10:53:51 -0400 Subject: [PATCH 078/111] xo-reflect: cosmetic: code layout --- include/xo/reflect/SelfTagging.hpp | 32 +++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/include/xo/reflect/SelfTagging.hpp b/include/xo/reflect/SelfTagging.hpp index b41d4ce3..12eb279f 100644 --- a/include/xo/reflect/SelfTagging.hpp +++ b/include/xo/reflect/SelfTagging.hpp @@ -10,22 +10,22 @@ #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 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 */ From fba8ed9c895d473cc249a3e90b38a5743108e87c Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 14 Jun 2024 10:54:14 -0400 Subject: [PATCH 079/111] xo-reflect: github: fix title for cmake CI --- .github/workflows/cmake-single-platform.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index 77554e15..49c1a81c 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -1,6 +1,4 @@ -# This starter workflow is for a CMake project running on a single platform. There is a different starter workflow if you need cross-platform coverage. -# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-multi-platform.yml -name: CMake on a single platform +name: xo-reflect ubuntu build on: push: From 188825d449dff635b2d30069bc504b9adf19f050 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 14 Jun 2024 10:54:35 -0400 Subject: [PATCH 080/111] xo-reflect: cosmetic: code layout --- src/reflect/atomic/AtomicTdx.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/reflect/atomic/AtomicTdx.cpp b/src/reflect/atomic/AtomicTdx.cpp index 42ca9e2f..2e04ab4d 100644 --- a/src/reflect/atomic/AtomicTdx.cpp +++ b/src/reflect/atomic/AtomicTdx.cpp @@ -4,21 +4,22 @@ #include "TaggedPtr.hpp" namespace xo { - namespace reflect { - std::unique_ptr AtomicTdx::make() { - return std::unique_ptr(new AtomicTdx()); - } /*make*/ + namespace reflect { + std::unique_ptr + AtomicTdx::make() { + return std::unique_ptr(new AtomicTdx()); + } /*make*/ - TaggedPtr - AtomicTdx::child_tp(uint32_t /*i*/, void * /*object*/) const { - return TaggedPtr::universal_null(); - } /*child_tp*/ + TaggedPtr + AtomicTdx::child_tp(uint32_t /*i*/, void * /*object*/) const { + return TaggedPtr::universal_null(); + } /*child_tp*/ - std::string const & - AtomicTdx::struct_member_name(uint32_t i) const { - return TypeDescrExtra::struct_member_name(i); - } /*struct_member_name*/ - } /*namespace reflect*/ + 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 */ From aa55e20e1279800980bba44e736c2a4af0cc560a Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 14 Jun 2024 10:55:08 -0400 Subject: [PATCH 081/111] xo-reflect: + infra for reflecting functions --- include/xo/reflect/Metatype.hpp | 57 +++++++++++---------- include/xo/reflect/function/FunctionTdx.hpp | 49 ++++++++++++++++++ src/reflect/function/FunctionTdx.cpp | 37 +++++++++++++ 3 files changed, 116 insertions(+), 27 deletions(-) create mode 100644 include/xo/reflect/function/FunctionTdx.hpp create mode 100644 src/reflect/function/FunctionTdx.cpp diff --git a/include/xo/reflect/Metatype.hpp b/include/xo/reflect/Metatype.hpp index 23f3fd10..549d098e 100644 --- a/include/xo/reflect/Metatype.hpp +++ b/include/xo/reflect/Metatype.hpp @@ -5,34 +5,37 @@ #include namespace xo { - namespace reflect { - enum class Metatype { mt_invalid, mt_atomic, mt_pointer, mt_vector, mt_struct }; + 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; - default: - os << "???"; - } - return os; - } /*operator<<*/ - - } /*namespace reflect*/ + 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 */ diff --git a/include/xo/reflect/function/FunctionTdx.hpp b/include/xo/reflect/function/FunctionTdx.hpp new file mode 100644 index 00000000..1558328d --- /dev/null +++ b/include/xo/reflect/function/FunctionTdx.hpp @@ -0,0 +1,49 @@ +/** @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 **/ + static std::unique_ptr make_function(TypeDescr retval_td, + std::vector arg_td_v); + + + + // ----- Inherited from TypeDescrExtra ----- + + virtual Metatype metatype() const override { return Metatype::mt_function; } + virtual uint32_t n_child(void * /*object*/) const override { return 0; } + virtual TaggedPtr child_tp(uint32_t i, void * object) const override; + const std::string & struct_member_name(uint32_t i) const override; + + virtual uint32_t n_fn_arg() const override { return arg_td_v_.size(); } + virtual TypeDescr fn_retval() const override { return retval_td_; } + virtual TypeDescr fn_arg(uint32_t i) const override { return arg_td_v_[i]; } + + private: + FunctionTdx(TypeDescr retval_td, + std::vector arg_td_v); + + private: + /** function return value **/ + TypeDescr retval_td_; + /** function arguments, in positional order **/ + std::vector arg_td_v_; + }; /*FunctionTdx*/ + } /*namespace reflect*/ +} /*namespace xo*/ + + +/** end FunctionTdx.hpp **/ diff --git a/src/reflect/function/FunctionTdx.cpp b/src/reflect/function/FunctionTdx.cpp new file mode 100644 index 00000000..1b715fcd --- /dev/null +++ b/src/reflect/function/FunctionTdx.cpp @@ -0,0 +1,37 @@ +/* @file FunctionTdx.cpp */ + +#include "function/FunctionTdx.hpp" +#include "TaggedPtr.hpp" + +namespace xo { + namespace reflect { + /** create instance. Will be invoked exactly once for each reflected function type **/ + std::unique_ptr + FunctionTdx::make_function(TypeDescr retval_td, + std::vector arg_td_v) + { + return std::unique_ptr(new FunctionTdx(retval_td, std::move(arg_td_v))); + } + + FunctionTdx::FunctionTdx(TypeDescr retval_td, + std::vector arg_td_v) + : retval_td_{retval_td}, + arg_td_v_{std::move(arg_td_v)} + {} + + TaggedPtr + FunctionTdx::child_tp(uint32_t /*i*/, void * /*object*/) const + { + return TaggedPtr::universal_null(); + } + + const std::string & + FunctionTdx::struct_member_name(uint32_t i) const + { + return TypeDescrExtra::struct_member_name(i); + } + } /*namespace reflect*/ +} /*namespace xo*/ + + +/* end FunctionTdx.cpp */ From ec365d8b6f6f68e729c8393ec8d64685ee1f050c Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 14 Jun 2024 10:55:36 -0400 Subject: [PATCH 082/111] xo-reflect: build: ++ FunctionTdx.cpp --- src/reflect/CMakeLists.txt | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/reflect/CMakeLists.txt b/src/reflect/CMakeLists.txt index 397876fa..87417066 100644 --- a/src/reflect/CMakeLists.txt +++ b/src/reflect/CMakeLists.txt @@ -7,20 +7,13 @@ set(SELF_SRCS 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}) - -# ---------------------------------------------------------------- -# dependencies: indentlog, ... - xo_dependency(${SELF_LIB} refcnt) xo_dependency(${SELF_LIB} indentlog) xo_dependency(${SELF_LIB} subsys) - -# ---------------------------------------------------------------- -# 3rd party dependency: boost: - #xo_boost_dependency(${SELF_LIB}) # end CMakeLists.txt From ef191b136ebf5cee087ee19855cbfbbf4460253a Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 14 Jun 2024 10:56:35 -0400 Subject: [PATCH 083/111] xo-reflect: automatically reflecct function pointers --- include/xo/reflect/EstablishTypeDescr.hpp | 89 ++++++++++-------- include/xo/reflect/Reflect.hpp | 84 +++++++++++++++-- include/xo/reflect/TaggedPtr.hpp | 12 ++- include/xo/reflect/TypeDescr.hpp | 11 +++ include/xo/reflect/TypeDescrExtra.hpp | 108 ++++++++++++---------- 5 files changed, 206 insertions(+), 98 deletions(-) diff --git a/include/xo/reflect/EstablishTypeDescr.hpp b/include/xo/reflect/EstablishTypeDescr.hpp index 32e546d1..13c80999 100644 --- a/include/xo/reflect/EstablishTypeDescr.hpp +++ b/include/xo/reflect/EstablishTypeDescr.hpp @@ -9,53 +9,66 @@ #include "TaggedPtr.hpp" namespace xo { - namespace reflect { - class EstablishTypeDescr { - public: - /* implementation method; expect this to be used only within reflect/ library. - * avoids some otherwise-cyclic #include paths - * between specialized headers such as vector/VectorTdx.hpp and this - * EstablishTypeDescr.hpp - */ + namespace reflect { + /** @class EstablishTypeDescr + * @brief class to establish globally-unique TypeDescr object for a type T + * + * We don't require the full definition of T to use EstablishTypeDescr::establish(). + * In particular, a forward declaration is sufficient. + * + * Additional information (that depends on full definition) may be attached later, + * by assigning (once-only) to @ref TypeDescr::tdextra_ + * + * @note Application code will use @ref Reflect::require; that in turn relies on the + * template @ref EstablishTdx to leverage template pattern-matching for + * recurring patterns. + **/ + class EstablishTypeDescr { + public: + /* implementation method; expect this to be used only within reflect/ library. + * avoids some otherwise-cyclic #include paths + * between specialized headers such as vector/VectorTdx.hpp and this + * EstablishTypeDescr.hpp + */ #ifdef OBSOLETE - template - static TaggedPtr establish_tp(T * x) { return TaggedPtr(establish(), x); } + template + static TaggedPtr establish_tp(T * x) { return TaggedPtr(establish(), x); } #endif - template - static TaggedPtr establish_most_derived_tp(T * x) { return establish()->most_derived_self_tp(x); } + template + static TaggedPtr establish_most_derived_tp(T * x) { return establish()->most_derived_self_tp(x); } - template - static TypeDescrW establish() { - TypeDescrW td = TypeDescrBase::require(&typeid(T), - type_name(), - nullptr); + template + static TypeDescrW establish() { + TypeDescrW td = TypeDescrBase::require(&typeid(T), + type_name(), + nullptr); #ifdef NOT_USING - std::function to_self_tp; + std::function to_self_tp; - if (std::is_base_of_v) { - /* T is a descendant of SelfTagging (or T = SelfTagging); - * use SelfTagging.self_tp() - */ - to_self_tp = [](void * x) { return reinterpret_cast(x)->self_tp(); }; - } else { - /* T is not a descendant of SelfTagging. - * want to return - */ - to_self_tp = [td](void * x) { return TaggedPtr(td, x); }; - } + if (std::is_base_of_v) { + /* T is a descendant of SelfTagging (or T = SelfTagging); + * use SelfTagging.self_tp() + */ + to_self_tp = [](void * x) { return reinterpret_cast(x)->self_tp(); }; + } else { + /* T is not a descendant of SelfTagging. + * want to return + */ + to_self_tp = [td](void * x) { return TaggedPtr(td, x); }; + } - td->assign_to_self_tp(to_self_tp); + td->assign_to_self_tp(to_self_tp); #endif - return td; - } - }; /*EstablishTypeDescr*/ + return td; + } + }; /*EstablishTypeDescr*/ - template - inline TaggedPtr establish_most_derived_tp(T * x) { - return EstablishTypeDescr::establish_most_derived_tp(x); - } - } /*namespace reflect*/ + template + inline TaggedPtr establish_most_derived_tp(T * x) { + return EstablishTypeDescr::establish_most_derived_tp(x); + } + } /*namespace reflect*/ } /*namespace xo*/ diff --git a/include/xo/reflect/Reflect.hpp b/include/xo/reflect/Reflect.hpp index 89a75374..bba62ae1 100644 --- a/include/xo/reflect/Reflect.hpp +++ b/include/xo/reflect/Reflect.hpp @@ -11,6 +11,7 @@ #include "pointer/PointerTdx.hpp" #include "vector/VectorTdx.hpp" #include "struct/StructTdx.hpp" +#include "function/FunctionTdx.hpp" #include "xo/refcnt/Refcounted.hpp" #include #include @@ -21,44 +22,69 @@ namespace xo { template class EstablishTdx { public: + /** Create auxiliary reflection info for type @tparam T, + * once full definition is available. + * + * This includes: + * - metatype + * - component structure (types for navigable component objects) + * + **/ static std::unique_ptr make() { return AtomicTdx::make(); } - }; /*EstablishTdx*/ + }; // ----- xo::ref::rp ----- - /* definition provide after decl for Reflect {} below */ template class EstablishTdx> { public: + /* definition provide after decl for Reflect {} below */ static std::unique_ptr make(); - }; /*EstablishTdx*/ + }; // ----- std::array ----- - /* definition provide after decl for Reflect {} below */ template class EstablishTdx> { public: + /* definition provide after decl for Reflect {} below */ static std::unique_ptr make(); - }; /*EstablishTdx*/ + }; // ----- std::vector ----- - /* definition provide after decl for Reflect {} below */ template class EstablishTdx> { public: + /* definition provide after decl for Reflect {} below */ static std::unique_ptr make(); - }; /*EstablishTdx*/ + }; // ----- std::pair ----- - /* definition provide after decl for Reflect {} below */ template class EstablishTdx> { public: + /* definition provide after decl for Reflect {} below */ static std::unique_ptr make(); - }; /*EstablishTdx*/ + }; + + // ----- Retval (*)(A1 .. An) ----- + + template + class EstablishTdx { + public: + /* definition provided after decl for Reflect {} below */ + static std::unique_ptr make(); + }; + + // ----- Retval (*)() ----- + + template + class EstablishTdx { + /* definition provided after decl for Reflect {} below */ + static std::unique_ptr make(); + }; // ----- MakeTagged ----- @@ -229,6 +255,46 @@ namespace xo { return StructTdx::pair(); } /*make*/ + + // ----- Retval (*) (A1 .. An) ----- + + namespace detail { + /** @class AssembleArgv + * @brief create vector of complete TypeDescr objects comprising all template arguments + * + * Use: + * std::vector v; + * AssembleArgv::append_argv(&v); + * // do something with v + **/ + template + struct AssembleArgv; + + template <> + struct AssembleArgv<> { + static void append_argv(std::vector * p_v) {} + }; + + template + struct AssembleArgv { + static void append_argv(std::vector * p_v) { + p_v->push_back(Reflect::require()); + AssembleArgv::append_argv(p_v); + } + }; + } /*detail*/ + + /* declared above before + * class Reflect { ... } + */ + template + std::unique_ptr + EstablishTdx::make() { + std::vector argv; + detail::AssembleArgv::append_argv(&argv); + + return FunctionTdx::make_function(Reflect::require(), std::move(argv)); + } } /*namespace reflect*/ } /*namespace xo*/ diff --git a/include/xo/reflect/TaggedPtr.hpp b/include/xo/reflect/TaggedPtr.hpp index 7bb520aa..dce3fa06 100644 --- a/include/xo/reflect/TaggedPtr.hpp +++ b/include/xo/reflect/TaggedPtr.hpp @@ -59,9 +59,10 @@ namespace xo { void assign_address(void * x) { address_ = x; } bool is_universal_null() const { return (td_ == nullptr) && (address_ == nullptr); } + bool is_pointer() const { return td_ && td_->is_pointer(); } bool is_vector() const { return td_ && td_->is_vector(); } bool is_struct() const { return td_ && td_->is_struct(); } - + bool is_function() const { return td_ && td_->is_function(); } /* returns pointer-to-T, if in fact this tagged pointer is understood * to refer to a T-instance; otherwise nullptr @@ -71,11 +72,16 @@ namespace xo { uint32_t n_child() const { return this->td_->n_child(this->address_); - } /*n_child*/ + } TaggedPtr get_child(uint32_t i) const { return this->td_->child_tp(i, this->address_); - } /*get_child*/ + } + + /* if reflected function (.is_function() = true): + * number of arguments to that function + */ + uint32_t n_fn_arg() const { return this->td_->n_fn_arg(); } /* require: * - .is_struct() is true diff --git a/include/xo/reflect/TypeDescr.hpp b/include/xo/reflect/TypeDescr.hpp index 94aa3aa3..c50a6d8d 100644 --- a/include/xo/reflect/TypeDescr.hpp +++ b/include/xo/reflect/TypeDescr.hpp @@ -179,8 +179,10 @@ namespace xo { } } /*recover_native*/ + bool is_pointer() const { return this->tdextra_->is_pointer(); } bool is_vector() const { return this->tdextra_->is_vector(); } bool is_struct() const { return this->tdextra_->is_struct(); } + bool is_function() const { return this->tdextra_->is_function(); } /* given a T-instance object, return tagged pointer with T replaced * by the most-derived-subtype of T to which *object belongs. @@ -203,6 +205,7 @@ namespace xo { std::string const & struct_member_name(uint32_t i) const { return this->tdextra_->struct_member_name(i); } + /* fetch runtime description for i'th reflected instance variable. * * require: @@ -216,6 +219,14 @@ namespace xo { return *sm; } /*struct_member*/ + uint32_t n_fn_arg() const { return this->tdextra_->n_fn_arg(); } + + /* require: + * - .is_function() = true + */ + TypeDescr fn_retval() const { return this->tdextra_->fn_retval(); } + TypeDescr fn_arg(uint32_t i) const { return this->tdextra_->fn_arg(i); } + void display(std::ostream & os) const; std::string display_string() const; diff --git a/include/xo/reflect/TypeDescrExtra.hpp b/include/xo/reflect/TypeDescrExtra.hpp index 824cd5d4..54db3025 100644 --- a/include/xo/reflect/TypeDescrExtra.hpp +++ b/include/xo/reflect/TypeDescrExtra.hpp @@ -8,58 +8,70 @@ #include namespace xo { - namespace reflect { - /* forward-declaring here. see [reflect/struct/StructMember.hpp] */ - class StructMember; - class TypeDescrBase; - class TaggedPtr; + namespace reflect { + /* forward-declaring here. see [reflect/struct/StructMember.hpp] */ + class StructMember; + class TypeDescrBase; + class TaggedPtr; - /* information associated with a c++ type. - * distinct from TypeDescrImpl: - * 1. want to use reflection to support for runtime polymorphism over similar but - * not directly-related types: for example - * std::vector - * and - * std::list - * are both ordered collections - * 2. some information can't be universally established via template-fu, - * for example struct member names - * 3. descriptions for recursive types require 2-stage construction - * - * A TypeDescrImpl instance will contain a pointer to a suitable - * TypeDescrExtra instance. - * - * The single TypeDescrImpl instance for some type T can be established - * automatically, see Reflect::require(). - * - * A specific TypeDescrExtra instance may be attached in a non-automated way - * later - */ - class TypeDescrExtra { - public: - using uint32_t = std::uint32_t; + /* information associated with a c++ type. + * distinct from TypeDescrImpl: + * 1. want to use reflection to support for runtime polymorphism over similar but + * not directly-related types: for example + * std::vector + * and + * std::list + * are both ordered collections + * 2. some information can't be universally established via template-fu, + * for example struct member names + * 3. descriptions for recursive types require 2-stage construction + * + * A TypeDescrImpl instance will contain a pointer to a suitable + * TypeDescrExtra instance. + * + * The single TypeDescrImpl instance for some type T can be established + * automatically, see Reflect::require(). + * + * A specific TypeDescrExtra instance may be attached in a non-automated way + * later + */ + class TypeDescrExtra { + public: + using uint32_t = std::uint32_t; - public: - virtual ~TypeDescrExtra() = default; + public: + virtual ~TypeDescrExtra() = default; - bool is_vector() const { return this->metatype() == Metatype::mt_vector; } - bool is_struct() const { return this->metatype() == Metatype::mt_struct; } + bool is_pointer() const { return this->metatype() == Metatype::mt_pointer; } + bool is_vector() const { return this->metatype() == Metatype::mt_vector; } + bool is_struct() const { return this->metatype() == Metatype::mt_struct; } + bool is_function() const { return this->metatype() == Metatype::mt_function; } - virtual Metatype metatype() const = 0; - /* given a T-instance, report most-derived subtype of T to which *object belongs. - * this works only for types that are derived from reflect::SelfTagging. - */ - virtual TaggedPtr most_derived_self_tp(TypeDescrBase const * object_td, void * object) const; - virtual uint32_t n_child(void * object) const = 0; - virtual TaggedPtr child_tp(uint32_t i, void * object) const = 0; - /* require: - * .is_struct() - */ - virtual std::string const & struct_member_name(uint32_t i) const = 0; - /* nullptr unless *this represents a struct/class type */ - virtual StructMember const * struct_member(uint32_t i) const; - }; /*TypeDescrExtra*/ - } /*namespace reflect*/ + virtual Metatype metatype() const = 0; + /* given a T-instance, report most-derived subtype of T to which *object belongs. + * this works only for types that are derived from reflect::SelfTagging. + */ + virtual TaggedPtr most_derived_self_tp(TypeDescrBase const * object_td, void * object) const; + virtual uint32_t n_child(void * object) const = 0; + virtual TaggedPtr child_tp(uint32_t i, void * object) const = 0; + /* require: + * .is_struct() + */ + virtual std::string const & struct_member_name(uint32_t i) const = 0; + /* nullptr unless *this represents a struct/class type */ + virtual StructMember const * struct_member(uint32_t i) const; + + // methods for working with reflected functions/methods + + /** number of arguments to function-like value + * + * @pre @ref TypeDescrExtra::is_function() is true + **/ + virtual uint32_t n_fn_arg() const { return 0; } + virtual const TypeDescrBase * fn_retval() const { return nullptr; } + virtual const TypeDescrBase * fn_arg(uint32_t /*i_arg*/) const { return nullptr; } + }; /*TypeDescrExtra*/ + } /*namespace reflect*/ } /*namespace xo*/ /* end TypeDescrExtra.hpp */ From fb8f4fdec29cc102767cc77905a6162cb1730505 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 14 Jun 2024 10:56:50 -0400 Subject: [PATCH 084/111] xo-reflect: utest for function reflection --- CMakeLists.txt | 10 +--------- utest/CMakeLists.txt | 7 ++++++- utest/FunctionTdx.test.cpp | 40 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 10 deletions(-) create mode 100644 utest/FunctionTdx.test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 050bc94c..c2e8b74d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,15 +7,7 @@ project(reflect VERSION 0.1) include(GNUInstallDirs) include(cmake/xo-bootstrap-macros.cmake) -xo_cxx_toplevel_options2() - -# ---------------------------------------------------------------- -# cmake -DCMAKE_BUILD_TYPE=coverage -xo_toplevel_coverage_config2() - -# ---------------------------------------------------------------- -# cmake -DCMAKE_BUILD_TYPE=debug -xo_toplevel_debug_config2() +xo_cxx_toplevel_options3() # ---------------------------------------------------------------- # c++ settings diff --git a/utest/CMakeLists.txt b/utest/CMakeLists.txt index aef60b2f..edc997ea 100644 --- a/utest/CMakeLists.txt +++ b/utest/CMakeLists.txt @@ -1,7 +1,12 @@ # 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) +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}) diff --git a/utest/FunctionTdx.test.cpp b/utest/FunctionTdx.test.cpp new file mode 100644 index 00000000..278a2532 --- /dev/null +++ b/utest/FunctionTdx.test.cpp @@ -0,0 +1,40 @@ +/* @file FunctionTdx.test.cpp */ + +#include "xo/reflect/Reflect.hpp" +#include + +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>(); + + REQUIRE(Reflect::is_reflected() == 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() == &fn); + REQUIRE(tp.n_child() == 0); /*not a composite*/ + // REQUIRE(tp.child_td(0) == ... + REQUIRE(tp.td()->fn_retval() == Reflect::require()); + REQUIRE(tp.n_fn_arg() == 1); + REQUIRE(tp.td()->fn_arg(0) == Reflect::require()); + } /*TEST_CASE(function-reflect1)*/ + } /*namespace ut*/ +} /*namespace xo*/ + +/* end FunctionTdx.test.cpp */ From 6c856be0fc20d643dd643ed73c82a26f107cb05a Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sat, 15 Jun 2024 13:15:22 -0400 Subject: [PATCH 085/111] xo-reflect: README: howto using xo-build --- .gitignore | 2 ++ README.md | 51 ++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 13c0afb7..3d3a7826 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +# emacs workspace config +.projectile # clangd working space (see emacs+lsp) .cache # typical cmake build directory (source-tree-nephew) diff --git a/README.md b/README.md index ecbafe0c..528d10e0 100644 --- a/README.md +++ b/README.md @@ -2,28 +2,49 @@ ## Getting Started -### build + install dependencies +### build + install `xo-cmake` dependency -- [github/Rconybea/refcnt](https://github.com/Rconybea/refcnt) -- [github/Rconybea/subsys](https://github.com/Rconybea/subsys) +- [github/Rconybea/xo-cmake](https://github.com/Rconybea/xo-cmake) -### build + install +Installs a few cmake ingredients, along with a build assistant `xo-build` for XO projects such as this one. + +### build + install other XO dependencies ``` -$ cd reflect -$ mkdir build -$ cd build -$ INSTALL_PREFIX=/usr/local # or wherever you prefer -$ cmake -DCMAKE_MODULE_PATH=${INSTALL_PREFIX}/share/cmake -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} .. -$ make -$ make install +$ xo-build --clone --configure --build --install xo-indentlog +$ xo-build --clone --configure --build --install xo-refnct +$ xo-build --clone --configure --build --install xo-subsys +``` + +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 ``` -$ cd xo-reflect -$ mkdir build-ccov -$ cd build-ccov -$ cmake -DCMAKE_MODULE_PATH=${INSTALL_PREFIX}/share/cmake -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} -DCODE_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug .. +$ cmake -DCMAKE_BUILD_TYPE=coverage -DCMAKE_INSTALL_PREFIX=$PREFIX xo-reflect/.build-ccov +$ cmake --build xo-reflect/.build-ccov ``` ### LSP support From 515cedf16043ce974455c1a07201963cf7d25532 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sat, 15 Jun 2024 13:15:57 -0400 Subject: [PATCH 086/111] xo-reflect: + EstablishFunctionTdx + bugfix deducing fptr inputs --- include/xo/reflect/Reflect.hpp | 110 ++++++++++++++++++-- include/xo/reflect/TypeDescr.hpp | 1 + include/xo/reflect/TypeDescrExtra.hpp | 3 +- include/xo/reflect/function/FunctionTdx.hpp | 18 +++- src/reflect/function/FunctionTdx.cpp | 19 +++- 5 files changed, 133 insertions(+), 18 deletions(-) diff --git a/include/xo/reflect/Reflect.hpp b/include/xo/reflect/Reflect.hpp index bba62ae1..20e862c9 100644 --- a/include/xo/reflect/Reflect.hpp +++ b/include/xo/reflect/Reflect.hpp @@ -72,16 +72,38 @@ namespace xo { // ----- Retval (*)(A1 .. An) ----- template - class EstablishTdx { + class EstablishTdx { public: /* definition provided after decl for Reflect {} below */ static std::unique_ptr make(); }; - // ----- Retval (*)() ----- + // ----- Retval (*)(A1 .. An) noexcept ----- - template - class EstablishTdx { + template + class EstablishTdx { + public: + /* definition provided after decl for Reflect {} below */ + static std::unique_ptr make(); + }; + + // ----- EstasblishFunctionTdx ------------------------------------- + + template + class EstablishFunctionTdx; + + // ----- Retval (*)(A1 .. An) ----- + + template + class EstablishFunctionTdx { + public: + /* definition provided after decl for Reflect {} below */ + static std::unique_ptr make(); + }; + + template + class EstablishFunctionTdx { + public: /* definition provided after decl for Reflect {} below */ static std::unique_ptr make(); }; @@ -170,6 +192,41 @@ namespace xo { return retval_td; } /*require*/ + /* can optionally use this when reflecting a function pointer. + * Should get the same result as reflect(), + * but will not fallback to AtomicTdx if T is not recognized as a function pointer + */ + template + static TypeDescrW require_function() { + //static_assert(std::is_function_v); + + TypeDescrW retval_td = EstablishTypeDescr::establish(); + + /* mark TypeDescr for T as complete (even though it isn't quite yet), + * so that when we encounter recursive types, reflection terminates. + * For example consider type resulting from code like + * + * typename T; + * using T = std::vector; + * + */ + if (retval_td->mark_complete()) { + /* control here on 2nd+later calls to require(). + * in principle can immediately short-circuit. + */ + } else { + /* control comes here the first time require() runs */ + + auto final_tdx = EstablishFunctionTdx::make(); + + retval_td->assign_tdextra(std::move(final_tdx)); + + /* also need to require for each child */ + } + + return retval_td; + } + /* Use: * T * xyz = ...; * TaggedPtr xyz_tp = Reflect::make_tp(xyz); @@ -256,7 +313,7 @@ namespace xo { return StructTdx::pair(); } /*make*/ - // ----- Retval (*) (A1 .. An) ----- + // ----- Retval(A1 .. An) ----- namespace detail { /** @class AssembleArgv @@ -272,7 +329,7 @@ namespace xo { template <> struct AssembleArgv<> { - static void append_argv(std::vector * p_v) {} + static void append_argv(std::vector * /*p_v*/) {} }; template @@ -284,6 +341,28 @@ namespace xo { }; } /*detail*/ + template + std::unique_ptr + EstablishFunctionTdx::make() { + std::vector argv; + detail::AssembleArgv::append_argv(&argv); + + return FunctionTdx::make_function(Reflect::require(), + std::move(argv), + false /*!is_noexcept*/); + } + + template + std::unique_ptr + EstablishFunctionTdx::make() { + std::vector argv; + detail::AssembleArgv::append_argv(&argv); + + return FunctionTdx::make_function(Reflect::require(), + std::move(argv), + true /*is_noexcept*/); + } + /* declared above before * class Reflect { ... } */ @@ -293,8 +372,25 @@ namespace xo { std::vector argv; detail::AssembleArgv::append_argv(&argv); - return FunctionTdx::make_function(Reflect::require(), std::move(argv)); + return FunctionTdx::make_function(Reflect::require(), + std::move(argv), + false /*!is_noexcept*/); } + + /* declared above before + * class Reflect { ... } + */ + template + std::unique_ptr + EstablishTdx::make() { + std::vector argv; + detail::AssembleArgv::append_argv(&argv); + + return FunctionTdx::make_function(Reflect::require(), + std::move(argv), + true /*is_noexcept*/); + } + } /*namespace reflect*/ } /*namespace xo*/ diff --git a/include/xo/reflect/TypeDescr.hpp b/include/xo/reflect/TypeDescr.hpp index c50a6d8d..54013654 100644 --- a/include/xo/reflect/TypeDescr.hpp +++ b/include/xo/reflect/TypeDescr.hpp @@ -226,6 +226,7 @@ namespace xo { */ 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; diff --git a/include/xo/reflect/TypeDescrExtra.hpp b/include/xo/reflect/TypeDescrExtra.hpp index 54db3025..c16257b3 100644 --- a/include/xo/reflect/TypeDescrExtra.hpp +++ b/include/xo/reflect/TypeDescrExtra.hpp @@ -67,9 +67,10 @@ namespace xo { * * @pre @ref TypeDescrExtra::is_function() is true **/ - virtual uint32_t n_fn_arg() const { return 0; } virtual const TypeDescrBase * fn_retval() const { return nullptr; } + virtual 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*/ diff --git a/include/xo/reflect/function/FunctionTdx.hpp b/include/xo/reflect/function/FunctionTdx.hpp index 1558328d..9c5523a6 100644 --- a/include/xo/reflect/function/FunctionTdx.hpp +++ b/include/xo/reflect/function/FunctionTdx.hpp @@ -15,11 +15,15 @@ namespace xo { public: virtual ~FunctionTdx() = default; - /** create instance. Will be invoked exactly once for each reflected function type **/ + /** 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 make_function(TypeDescr retval_td, - std::vector arg_td_v); - - + std::vector arg_td_v, + bool is_noexcept); // ----- Inherited from TypeDescrExtra ----- @@ -28,12 +32,14 @@ namespace xo { virtual TaggedPtr child_tp(uint32_t i, void * object) const override; const std::string & struct_member_name(uint32_t i) const override; - virtual uint32_t n_fn_arg() const override { return arg_td_v_.size(); } virtual TypeDescr fn_retval() const override { return retval_td_; } + virtual uint32_t n_fn_arg() const override { return arg_td_v_.size(); } virtual TypeDescr fn_arg(uint32_t i) const override { return arg_td_v_[i]; } + virtual bool fn_is_noexcept() const override { return is_noexcept_; } private: FunctionTdx(TypeDescr retval_td, + bool is_noexcept, std::vector arg_td_v); private: @@ -41,6 +47,8 @@ namespace xo { TypeDescr retval_td_; /** function arguments, in positional order **/ std::vector arg_td_v_; + /** true iff function promises never to throw **/ + bool is_noexcept_ = false; }; /*FunctionTdx*/ } /*namespace reflect*/ } /*namespace xo*/ diff --git a/src/reflect/function/FunctionTdx.cpp b/src/reflect/function/FunctionTdx.cpp index 1b715fcd..bfd43673 100644 --- a/src/reflect/function/FunctionTdx.cpp +++ b/src/reflect/function/FunctionTdx.cpp @@ -5,19 +5,28 @@ namespace xo { namespace reflect { - /** create instance. Will be invoked exactly once for each reflected function type **/ + /** create instance. Will be invoked exactly once for each reflected function type **/ std::unique_ptr FunctionTdx::make_function(TypeDescr retval_td, - std::vector arg_td_v) + std::vector arg_td_v, + bool is_noexcept) { - return std::unique_ptr(new FunctionTdx(retval_td, std::move(arg_td_v))); + return std::unique_ptr(new FunctionTdx(retval_td, + is_noexcept, + std::move(arg_td_v))); } FunctionTdx::FunctionTdx(TypeDescr retval_td, + bool is_noexcept, std::vector arg_td_v) : retval_td_{retval_td}, - arg_td_v_{std::move(arg_td_v)} - {} + arg_td_v_{std::move(arg_td_v)}, + is_noexcept_{is_noexcept} + { + if (!retval_td) { + throw std::runtime_error("FunctionTdx::ctor: null return type?"); + } + } TaggedPtr FunctionTdx::child_tp(uint32_t /*i*/, void * /*object*/) const From 7243939916545eaf839238d4b1b1dcdbb9eb6ad3 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 18 Jun 2024 14:05:51 -0400 Subject: [PATCH 087/111] xo-reflect: allow TypeDescr without .native_typeinfo --- README.md | 2 +- include/xo/reflect/Reflect.hpp | 9 ++++ include/xo/reflect/TaggedPtr.hpp | 2 +- include/xo/reflect/TypeDescr.hpp | 79 ++++++++++++++++++++++++++++---- src/reflect/TypeDescr.cpp | 4 +- 5 files changed, 83 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 528d10e0..370cbafb 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ or equivalently $ git clone git@github.com:Rconybea/xo-reflect.git ``` -### build + install xo-reflect +### build + install `xo-reflect` ``` $ xo-build --configure --build --install xo-reflect ``` diff --git a/include/xo/reflect/Reflect.hpp b/include/xo/reflect/Reflect.hpp index 20e862c9..e10519a8 100644 --- a/include/xo/reflect/Reflect.hpp +++ b/include/xo/reflect/Reflect.hpp @@ -227,6 +227,15 @@ namespace xo { return retval_td; } + /** given address @p src_address of a value with type described by @p src, + * return typed pointer of type @tparam T, provided that @p src_td + * actually describes @tparam T. Otherwise returns nullptr + **/ + template + static T * recover_native(TypeDescr src_td, void * src_address) { + return TaggedPtr(src_td, src_address).recover_native(); + } + /* Use: * T * xyz = ...; * TaggedPtr xyz_tp = Reflect::make_tp(xyz); diff --git a/include/xo/reflect/TaggedPtr.hpp b/include/xo/reflect/TaggedPtr.hpp index dce3fa06..fbffaa04 100644 --- a/include/xo/reflect/TaggedPtr.hpp +++ b/include/xo/reflect/TaggedPtr.hpp @@ -68,7 +68,7 @@ namespace xo { * to refer to a T-instance; otherwise nullptr */ template - T * recover_native() const { return this->td_->recover_native(this->address_); } + T * recover_native() const { return this->td_->recover_native2(this->td_, this->address_); } uint32_t n_child() const { return this->td_->n_child(this->address_); diff --git a/include/xo/reflect/TypeDescr.hpp b/include/xo/reflect/TypeDescr.hpp index 54013654..4d49876b 100644 --- a/include/xo/reflect/TypeDescr.hpp +++ b/include/xo/reflect/TypeDescr.hpp @@ -149,28 +149,66 @@ namespace xo { static void print_reflected_types(std::ostream & os); TypeId id() const { return id_; } - std::type_info const * typeinfo() const { return typeinfo_; } + std::type_info const * native_typeinfo() const { return native_typeinfo_; } std::string_view const & canonical_name() const { return canonical_name_; } std::string_view const & short_name() const { return short_name_; } bool complete_flag() const { return complete_flag_; } TypeDescrExtra * tdextra() const { return tdextra_.get(); } Metatype metatype() const { return tdextra_->metatype(); } - /* true iff the type represented by *this is the same as the type T. + /* 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 + [[deprecated]] bool is_native() const { - return ((this->typeinfo() == &typeid(T)) - || (this->typeinfo()->hash_code() == typeid(T).hash_code()) - || (this->typeinfo()->name() == typeid(T).name())); + 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(), + * then .canonical_name is computed by type_name() + * + * (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 */ + /** 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(). Application code should prefer any of: + * 1. recover_native2(src_td, src_address) + * 2. Reflect::recover_native(src_td, src_address) + * 3. TaggedPtr(src_td,src_address).recover_native() + * instead of src_td->recover_native() + * + * (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 + [[deprecated]] T * recover_native(void * address) const { if (this->is_native()) { return reinterpret_cast(address); @@ -179,6 +217,22 @@ namespace xo { } } /*recover_native*/ + /** safe downcast -- like dynamic_cast<>, but does not require a source type. + * + * Application code should prefer TaggedPtr::recover_native() + * + * TODO: need variation on this to correctly-handle function types, + * since for exampple cast from void* -> void (*)() is not allowed + **/ + template + T * recover_native2(TypeDescr address_td, void * address) const { + if (this == address_td) { + return reinterpret_cast(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(); } @@ -255,7 +309,9 @@ namespace xo { * - s_type_table_map[TypeInfoRef(x->typeinfo())] = x */ - /* hashmap of all TypeDescr instances, indexed by . singleton */ + /* hashmap of all native TypeDescr instances, indexed by typeinfo. + * singleton. + */ static std::unordered_map> s_type_table_map; /* hashmap of (presumed) duplicate TypeInfoRef values. * This happens with clang sometimes when the same type is referenced @@ -269,8 +325,13 @@ namespace xo { private: /* unique id# for this type */ TypeId id_; - /* typeinfo for type T */ - std::type_info const * typeinfo_ = nullptr; + /** typeinfo for type T, if available. nullptr otherwise. + * + * 1. Always available for type-descriptions constructed via Reflect::require. + * 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()) * e.g. * xo::option::Px2 diff --git a/src/reflect/TypeDescr.cpp b/src/reflect/TypeDescr.cpp index b0a0dfd7..8eaea8e0 100644 --- a/src/reflect/TypeDescr.cpp +++ b/src/reflect/TypeDescr.cpp @@ -124,11 +124,11 @@ namespace xo { } /*namespace*/ TypeDescrBase::TypeDescrBase(TypeId id, - std::type_info const * tinfo, + std::type_info const * native_tinfo, std::string_view canonical_name, std::unique_ptr tdextra) : id_{std::move(id)}, - typeinfo_{tinfo}, + native_typeinfo_{native_tinfo}, canonical_name_{std::move(canonical_name)}, short_name_{unqualified_name(canonical_name_)}, tdextra_{std::move(tdextra)} From dddd6ca5ec0d1f6e5773361d42652a10c49cfb74 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 18 Jun 2024 15:43:03 -0400 Subject: [PATCH 088/111] xo-reflect: refactor to support manually-constructed function types --- include/xo/reflect/EstablishTypeDescr.hpp | 2 +- include/xo/reflect/TypeDescr.hpp | 158 ++++++++++++++++---- include/xo/reflect/TypeDescrExtra.hpp | 2 + include/xo/reflect/function/FunctionTdx.hpp | 17 +-- src/reflect/TypeDescr.cpp | 158 ++++++++++++++------ src/reflect/function/FunctionTdx.cpp | 4 +- 6 files changed, 254 insertions(+), 87 deletions(-) diff --git a/include/xo/reflect/EstablishTypeDescr.hpp b/include/xo/reflect/EstablishTypeDescr.hpp index 13c80999..423345dc 100644 --- a/include/xo/reflect/EstablishTypeDescr.hpp +++ b/include/xo/reflect/EstablishTypeDescr.hpp @@ -40,7 +40,7 @@ namespace xo { template static TypeDescrW establish() { TypeDescrW td = TypeDescrBase::require(&typeid(T), - type_name(), + std::string(type_name()), nullptr); #ifdef NOT_USING diff --git a/include/xo/reflect/TypeDescr.hpp b/include/xo/reflect/TypeDescr.hpp index 4d49876b..a0c307be 100644 --- a/include/xo/reflect/TypeDescr.hpp +++ b/include/xo/reflect/TypeDescr.hpp @@ -116,6 +116,50 @@ namespace xo { } /*namespace detail*/ #endif + /* hashable contents of a FunctionTdx instance (without requiring decl of TypeDescrExtra), + * for unique-ification of manually-constructed function types + */ + struct FunctionTdxInfo { + FunctionTdxInfo() = default; + FunctionTdxInfo(TypeDescr retval_td, + const std::vector & 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; + } + + /** function return value **/ + TypeDescr retval_td_ = nullptr; + /** function arguments, in positional order **/ + std::vector arg_td_v_; + /** true iff function promises never to throw **/ + bool is_noexcept_ = false; + }; + } +} + +namespace xo { + namespace reflect { class TypeDescrExtra; /* run-time description for a native c++ type */ @@ -130,8 +174,8 @@ namespace xo { * introducing this for unit testing */ static bool is_reflected(std::type_info const * tinfo) { - return (s_type_table_map.find(TypeInfoRef(tinfo)) - != s_type_table_map.end()); + return (s_native_type_table_map.find(TypeInfoRef(tinfo)) + != s_native_type_table_map.end()); } /*is_reflected*/ /* NOTE: @@ -141,17 +185,17 @@ namespace xo { * See FAQ * [Build Issues|Q2 - dynamic_cast> fails] */ - static TypeDescrW require(std::type_info const * tinfo, - std::string_view canonical_name, + static TypeDescrW require(const std::type_info * tinfo, + const std::string & canonical_name, std::unique_ptr tdextra); /* print table of reflected types to os */ static void print_reflected_types(std::ostream & os); TypeId id() const { return id_; } - std::type_info const * native_typeinfo() const { return native_typeinfo_; } - std::string_view const & canonical_name() const { return canonical_name_; } - std::string_view const & short_name() const { return short_name_; } + 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(); } @@ -273,6 +317,8 @@ namespace xo { 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: @@ -298,29 +344,62 @@ namespace xo { private: TypeDescrBase(TypeId id, - std::type_info const * tinfo, - std::string_view canonical_name, + const std::type_info * tinfo, + const std::string & canonical_name, std::unique_ptr tdextra); + 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_type_table_map[TypeInfoRef(x->typeinfo())] = x + * - s_native_type_table_map[TypeInfoRef(x->typeinfo())] = x */ - /* hashmap of all native TypeDescr instances, indexed by typeinfo. - * singleton. - */ - static std::unordered_map> s_type_table_map; - /* hashmap of (presumed) duplicate TypeInfoRef values. - * This happens with clang sometimes when the same type is referenced - * from multiple modules (i.e. shared libs). - */ + /** vector of all TypeDescr instances, indexed by TypeId. singleton. **/ + static std::vector> 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 s_canonical_type_table_map; + + /** hashmap of all native TypeDescr instances, + * indexed by typeinfo. singleton. + **/ + static std::unordered_map 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 s_coalesced_type_table_map; - /* vector of all TypeDescr instances. singleton. */ - static std::vector s_type_table_v; + /** 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 s_function_type_map; private: /* unique id# for this type */ @@ -332,15 +411,20 @@ namespace xo { * see Lambda.cpp in xo-expression. **/ std::type_info const * native_typeinfo_ = nullptr; - /* canonical name for this type (see demangle.hpp for type_name()) + /** canonical name for this type (see demangle.hpp for type_name()) * e.g. * xo::option::Px2 - */ - std::string_view canonical_name_; - /* suffix of .canonical_name, just after last ':' - * e.g. - * Px2 - */ + * + * NOTE: if we only had to deal with types created via Reflect::reflect(), + * 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 @@ -380,4 +464,24 @@ namespace xo { } /*namespace reflect*/ } /*namespace xo*/ +namespace std { + /** @brief overload for hashing xo::reflect::FunctionTdxInfo objects + **/ + template <> + struct hash { + 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{}(x.retval_td_); + + for (std::size_t i = 0, n = x.arg_td_v_.size(); i < n; ++i) { + h = (h << 1) ^ hash{}(x.arg_td_v_[i]); + } + + h = (h << 1) ^ (x.is_noexcept_ ? 1 : 0); + + return h; + } + }; +} + /* end TypeDescr.hpp */ diff --git a/include/xo/reflect/TypeDescrExtra.hpp b/include/xo/reflect/TypeDescrExtra.hpp index c16257b3..bef9463e 100644 --- a/include/xo/reflect/TypeDescrExtra.hpp +++ b/include/xo/reflect/TypeDescrExtra.hpp @@ -11,6 +11,7 @@ namespace xo { namespace reflect { /* forward-declaring here. see [reflect/struct/StructMember.hpp] */ class StructMember; + class FunctionTdxInfo; class TypeDescrBase; class TaggedPtr; @@ -67,6 +68,7 @@ namespace xo { * * @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; } diff --git a/include/xo/reflect/function/FunctionTdx.hpp b/include/xo/reflect/function/FunctionTdx.hpp index 9c5523a6..116f3721 100644 --- a/include/xo/reflect/function/FunctionTdx.hpp +++ b/include/xo/reflect/function/FunctionTdx.hpp @@ -32,10 +32,11 @@ namespace xo { virtual TaggedPtr child_tp(uint32_t i, void * object) const override; const std::string & struct_member_name(uint32_t i) const override; - virtual TypeDescr fn_retval() const override { return retval_td_; } - virtual uint32_t n_fn_arg() const override { return arg_td_v_.size(); } - virtual TypeDescr fn_arg(uint32_t i) const override { return arg_td_v_[i]; } - virtual bool fn_is_noexcept() const override { return is_noexcept_; } + 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(TypeDescr retval_td, @@ -43,12 +44,8 @@ namespace xo { std::vector arg_td_v); private: - /** function return value **/ - TypeDescr retval_td_; - /** function arguments, in positional order **/ - std::vector arg_td_v_; - /** true iff function promises never to throw **/ - bool is_noexcept_ = false; + /** ingredients in complete function type description **/ + FunctionTdxInfo info_; }; /*FunctionTdx*/ } /*namespace reflect*/ } /*namespace xo*/ diff --git a/src/reflect/TypeDescr.cpp b/src/reflect/TypeDescr.cpp index 8eaea8e0..c541ea0a 100644 --- a/src/reflect/TypeDescr.cpp +++ b/src/reflect/TypeDescr.cpp @@ -15,69 +15,135 @@ namespace xo { uint32_t TypeId::s_next_id = 1; - std::unordered_map> - TypeDescrBase::s_type_table_map; + std::unordered_map + TypeDescrBase::s_function_type_map; + + std::unordered_map + TypeDescrBase::s_canonical_type_table_map; + + std::unordered_map + TypeDescrBase::s_native_type_table_map; std::unordered_map TypeDescrBase::s_coalesced_type_table_map; - std::vector + std::vector> TypeDescrBase::s_type_table_v; TypeDescrW - TypeDescrBase::require(std::type_info const * tinfo, - std::string_view canonical_name, + TypeDescrBase::require(const std::type_info * native_tinfo, + const std::string & canonical_name, std::unique_ptr tdextra) { - /* 1. lookup by tinfo hash_code in s_type_table_map */ - { - auto ix = s_type_table_map.find(TypeInfoRef(tinfo)); + 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_type_table_map.end()) && ix->second) - return ix->second.get(); - } + 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(tinfo)); + /* 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. O(n) lookup by canonical_name, before we create a new slot. - * - * Have to accept that on clang type_info objects aren't always unique (!$@#!!) - * - * TODO: lookup table keyed by canonical_name - */ - for (TypeDescrBase * x : s_type_table_v) { - if (x && (x->canonical_name() == canonical_name)) { - /* 1. assume *x represents the type associated with tinfo. - * 2. *do* store tinfo in s_coalesced_type_table_map[], - * for faster lookup next time - */ - s_coalesced_type_table_map[TypeInfoRef(tinfo)] = x; - - return x; + if ((ix != s_coalesced_type_table_map.end()) && ix->second) + return ix->second; } } - TypeId id = TypeId::allocate(); + /* 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); - std::unique_ptr & slot = s_type_table_map[TypeInfoRef(tinfo)]; + 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(); - slot.reset(new TypeDescrBase(id, - tinfo, - canonical_name, - std::move(tdextra))); + /* 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); - if (s_type_table_v.size() <= id.id()) - s_type_table_v.resize(id.id() + 1); + s_native_type_table_map[TypeInfoRef(native_tinfo)] + = ix->second; + } - s_type_table_v[id.id()] = slot.get(); + 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)); - return slot.get(); + 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)); + + 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*/ void @@ -85,7 +151,7 @@ namespace xo { { os << "display(os); @@ -124,8 +190,8 @@ namespace xo { } /*namespace*/ TypeDescrBase::TypeDescrBase(TypeId id, - std::type_info const * native_tinfo, - std::string_view canonical_name, + const std::type_info * native_tinfo, + const std::string & canonical_name, std::unique_ptr tdextra) : id_{std::move(id)}, native_typeinfo_{native_tinfo}, diff --git a/src/reflect/function/FunctionTdx.cpp b/src/reflect/function/FunctionTdx.cpp index bfd43673..c81041ab 100644 --- a/src/reflect/function/FunctionTdx.cpp +++ b/src/reflect/function/FunctionTdx.cpp @@ -19,9 +19,7 @@ namespace xo { FunctionTdx::FunctionTdx(TypeDescr retval_td, bool is_noexcept, std::vector arg_td_v) - : retval_td_{retval_td}, - arg_td_v_{std::move(arg_td_v)}, - is_noexcept_{is_noexcept} + : info_{retval_td, std::move(arg_td_v), is_noexcept} { if (!retval_td) { throw std::runtime_error("FunctionTdx::ctor: null return type?"); From 8a20796fe9156add78deda060dbc6fb0d48a4139 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 18 Jun 2024 17:14:38 -0400 Subject: [PATCH 089/111] xo-reflect: + Reflect::is_native() --- include/xo/reflect/Reflect.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/xo/reflect/Reflect.hpp b/include/xo/reflect/Reflect.hpp index e10519a8..23179c13 100644 --- a/include/xo/reflect/Reflect.hpp +++ b/include/xo/reflect/Reflect.hpp @@ -227,6 +227,12 @@ namespace xo { return retval_td; } + /** true iff @p src_td is a type-description for @tparam T **/ + template + static bool is_native(TypeDescr src_td) { + return (require() == src_td); + } + /** given address @p src_address of a value with type described by @p src, * return typed pointer of type @tparam T, provided that @p src_td * actually describes @tparam T. Otherwise returns nullptr From 6fc1fff9dc6ef6432c02e6474a2c3b24b05a6c32 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 18 Jun 2024 17:15:08 -0400 Subject: [PATCH 090/111] xo-reflect: + TypeDescr::lookup_by_name() --- include/xo/reflect/TypeDescr.hpp | 2 ++ src/reflect/TypeDescr.cpp | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/include/xo/reflect/TypeDescr.hpp b/include/xo/reflect/TypeDescr.hpp index a0c307be..cd596979 100644 --- a/include/xo/reflect/TypeDescr.hpp +++ b/include/xo/reflect/TypeDescr.hpp @@ -190,6 +190,8 @@ namespace xo { std::unique_ptr tdextra); /* print table of reflected types to os */ + /** lookup type by canonical name **/ + static TypeDescr lookup_by_name(const std::string & canonical_name); static void print_reflected_types(std::ostream & os); TypeId id() const { return id_; } diff --git a/src/reflect/TypeDescr.cpp b/src/reflect/TypeDescr.cpp index c541ea0a..7c2eb19f 100644 --- a/src/reflect/TypeDescr.cpp +++ b/src/reflect/TypeDescr.cpp @@ -146,6 +146,19 @@ namespace xo { return new_slot.get(); } /*require*/ + 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) { From f4db0eefce141f7943540bc41051cfbe3a974da7 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 18 Jun 2024 17:16:05 -0400 Subject: [PATCH 091/111] xo-reflect: + FunctionTdxInfo::make_canonical_name() --- include/xo/reflect/TypeDescr.hpp | 13 ++++++++----- src/reflect/TypeDescr.cpp | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/include/xo/reflect/TypeDescr.hpp b/include/xo/reflect/TypeDescr.hpp index cd596979..77303a83 100644 --- a/include/xo/reflect/TypeDescr.hpp +++ b/include/xo/reflect/TypeDescr.hpp @@ -148,18 +148,21 @@ namespace xo { 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 arg_td_v_; /** true iff function promises never to throw **/ bool is_noexcept_ = false; - }; - } -} + }; /*FunctionTdxInfo*/ -namespace xo { - namespace reflect { class TypeDescrExtra; /* run-time description for a native c++ type */ diff --git a/src/reflect/TypeDescr.cpp b/src/reflect/TypeDescr.cpp index 7c2eb19f..84579d5b 100644 --- a/src/reflect/TypeDescr.cpp +++ b/src/reflect/TypeDescr.cpp @@ -15,6 +15,25 @@ namespace xo { 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 TypeDescrBase::s_function_type_map; From 276e1e545115334b6d9283cb99c3687a00f41082 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 18 Jun 2024 17:16:28 -0400 Subject: [PATCH 092/111] xo-reflect: + TypeDescrBase::require_by_fn_info() --- include/xo/reflect/TypeDescr.hpp | 4 +++- src/reflect/TypeDescr.cpp | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/include/xo/reflect/TypeDescr.hpp b/include/xo/reflect/TypeDescr.hpp index 77303a83..4452f094 100644 --- a/include/xo/reflect/TypeDescr.hpp +++ b/include/xo/reflect/TypeDescr.hpp @@ -192,7 +192,9 @@ namespace xo { const std::string & canonical_name, std::unique_ptr tdextra); - /* print table of reflected types to os */ + /** 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); static void print_reflected_types(std::ostream & os); diff --git a/src/reflect/TypeDescr.cpp b/src/reflect/TypeDescr.cpp index 84579d5b..c9812240 100644 --- a/src/reflect/TypeDescr.cpp +++ b/src/reflect/TypeDescr.cpp @@ -165,6 +165,20 @@ namespace xo { 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(), + 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); From ef41f9b28074a5715d7886e09d373f209928cab4 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 18 Jun 2024 17:17:09 -0400 Subject: [PATCH 093/111] xo-reflect: + comment --- include/xo/reflect/TypeDescr.hpp | 2 ++ src/reflect/TypeDescr.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/include/xo/reflect/TypeDescr.hpp b/include/xo/reflect/TypeDescr.hpp index 4452f094..c85b2379 100644 --- a/include/xo/reflect/TypeDescr.hpp +++ b/include/xo/reflect/TypeDescr.hpp @@ -197,6 +197,8 @@ namespace xo { /** 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); TypeId id() const { return id_; } diff --git a/src/reflect/TypeDescr.cpp b/src/reflect/TypeDescr.cpp index c9812240..2ffb59a2 100644 --- a/src/reflect/TypeDescr.cpp +++ b/src/reflect/TypeDescr.cpp @@ -4,6 +4,7 @@ #include "TaggedPtr.hpp" #include "TypeDescrExtra.hpp" #include "atomic/AtomicTdx.hpp" +#include "function/FunctionTdx.hpp" #include "xo/indentlog/scope.hpp" namespace xo { From b06c55b98b73aea001264d56e1dd821a6837ba10 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 18 Jun 2024 17:17:26 -0400 Subject: [PATCH 094/111] xo-reflect: + FunctionTdx::make_function() --- include/xo/reflect/function/FunctionTdx.hpp | 8 +++++--- src/reflect/function/FunctionTdx.cpp | 20 ++++++++++++-------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/include/xo/reflect/function/FunctionTdx.hpp b/include/xo/reflect/function/FunctionTdx.hpp index 116f3721..5d2f57c2 100644 --- a/include/xo/reflect/function/FunctionTdx.hpp +++ b/include/xo/reflect/function/FunctionTdx.hpp @@ -24,6 +24,10 @@ namespace xo { static std::unique_ptr make_function(TypeDescr retval_td, std::vector arg_td_v, bool is_noexcept); + /** create instance from FunctionTdxInfo + * @param fn_info. function ingredients -- return type, arg types, noexcept + **/ + static std::unique_ptr make_function(const FunctionTdxInfo & fn_info); // ----- Inherited from TypeDescrExtra ----- @@ -39,9 +43,7 @@ namespace xo { virtual bool fn_is_noexcept() const override { return info_.is_noexcept_; } private: - FunctionTdx(TypeDescr retval_td, - bool is_noexcept, - std::vector arg_td_v); + FunctionTdx(const FunctionTdxInfo & fn_info); private: /** ingredients in complete function type description **/ diff --git a/src/reflect/function/FunctionTdx.cpp b/src/reflect/function/FunctionTdx.cpp index c81041ab..27c0f8fc 100644 --- a/src/reflect/function/FunctionTdx.cpp +++ b/src/reflect/function/FunctionTdx.cpp @@ -11,17 +11,21 @@ namespace xo { std::vector arg_td_v, bool is_noexcept) { - return std::unique_ptr(new FunctionTdx(retval_td, - is_noexcept, - std::move(arg_td_v))); + return make_function(FunctionTdxInfo(retval_td, + std::move(arg_td_v), + is_noexcept)); } - FunctionTdx::FunctionTdx(TypeDescr retval_td, - bool is_noexcept, - std::vector arg_td_v) - : info_{retval_td, std::move(arg_td_v), is_noexcept} + std::unique_ptr + FunctionTdx::make_function(const FunctionTdxInfo & fn_info) { - if (!retval_td) { + return std::unique_ptr(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?"); } } From 05c94a3105961e645efa7ba96f654c6d782da3a5 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 18 Jun 2024 17:30:56 -0400 Subject: [PATCH 095/111] xo-reflect: fix constructed canonical name for fptr --- src/reflect/TypeDescr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reflect/TypeDescr.cpp b/src/reflect/TypeDescr.cpp index 2ffb59a2..34cfa8e1 100644 --- a/src/reflect/TypeDescr.cpp +++ b/src/reflect/TypeDescr.cpp @@ -22,7 +22,7 @@ namespace xo { std::ostringstream ss; ss << retval_td_->canonical_name(); - ss << "(*)("; + ss << " (*)("; for (std::size_t i = 0, n = arg_td_v_.size(); i < n; ++i) { if (i > 0) ss << ","; From 57a75e380fd5e7bfec867d18f34d6e512c737c1c Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 19 Jun 2024 10:51:48 -0400 Subject: [PATCH 096/111] xo-reflect: + reflect builtin c++ types (int ..) automatically --- include/xo/reflect/TypeDescr.hpp | 15 +++++++++++++++ src/reflect/TypeDescr.cpp | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/include/xo/reflect/TypeDescr.hpp b/include/xo/reflect/TypeDescr.hpp index c85b2379..31c58063 100644 --- a/include/xo/reflect/TypeDescr.hpp +++ b/include/xo/reflect/TypeDescr.hpp @@ -200,6 +200,8 @@ namespace xo { /** 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_; } @@ -470,6 +472,19 @@ namespace xo { 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*/ diff --git a/src/reflect/TypeDescr.cpp b/src/reflect/TypeDescr.cpp index 34cfa8e1..41f5204e 100644 --- a/src/reflect/TypeDescr.cpp +++ b/src/reflect/TypeDescr.cpp @@ -3,6 +3,7 @@ #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" @@ -301,6 +302,20 @@ namespace xo { this->complete_flag_ = true; this->tdextra_ = std::move(tdx); } /*assign_tdextra*/ + + TypeDescrTable::TypeDescrTable() { + Reflect::require(); + Reflect::require(); + Reflect::require(); + Reflect::require(); + Reflect::require(); + Reflect::require(); + Reflect::require(); + Reflect::require(); + } /*ctor*/ + + TypeDescrTable + TypeDescrTable::s_instance; } /*namespace reflect*/ } /*namespace xo*/ From 0454fdcbff0e852ffd03fd83e53a32ff89fd9700 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 25 Jun 2024 00:26:05 -0400 Subject: [PATCH 097/111] xo-reflect: minor: move assign_tdextra() impl to .hpp --- include/xo/reflect/TypeDescr.hpp | 5 ++++- src/reflect/TypeDescr.cpp | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/include/xo/reflect/TypeDescr.hpp b/include/xo/reflect/TypeDescr.hpp index 31c58063..4591bbe2 100644 --- a/include/xo/reflect/TypeDescr.hpp +++ b/include/xo/reflect/TypeDescr.hpp @@ -351,7 +351,10 @@ namespace xo { /* call this once to attach extended type information to a type-description * (e.g. description of struct members for a record type) */ - void assign_tdextra(std::unique_ptr tdx); + void assign_tdextra(std::unique_ptr tdx) { + this->complete_flag_ = true; + this->tdextra_ = std::move(tdx); + } private: TypeDescrBase(TypeId id, diff --git a/src/reflect/TypeDescr.cpp b/src/reflect/TypeDescr.cpp index 41f5204e..9bb4bc39 100644 --- a/src/reflect/TypeDescr.cpp +++ b/src/reflect/TypeDescr.cpp @@ -288,6 +288,7 @@ namespace xo { return retval; } /*mark_complete*/ +#ifdef NOT_USING void TypeDescrBase::assign_tdextra(std::unique_ptr tdx) { @@ -302,6 +303,7 @@ namespace xo { this->complete_flag_ = true; this->tdextra_ = std::move(tdx); } /*assign_tdextra*/ +#endif TypeDescrTable::TypeDescrTable() { Reflect::require(); From a7b6ca28645b1e4f845a0d2a650991ba61805ead Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 25 Jun 2024 00:26:25 -0400 Subject: [PATCH 098/111] xo-reflect: + xo-reflectutil in README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 370cbafb..4e43dfd8 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Installs a few cmake ingredients, along with a build assistant `xo-build` for X $ 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 From 42331ce8bab4acb7af802292add90543f26e7855 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 25 Jun 2024 18:39:37 -0400 Subject: [PATCH 099/111] xo-reflect: multiple improvements -- kitchen sink --- include/xo/reflect/StructReflector.hpp | 277 +++++++-------- include/xo/reflect/TypeDescr.hpp | 1 + include/xo/reflect/TypeDescrExtra.hpp | 5 + include/xo/reflect/atomic/AtomicTdx.hpp | 41 +-- include/xo/reflect/function/FunctionTdx.hpp | 1 + include/xo/reflect/reflect_struct.hpp | 71 ++++ include/xo/reflect/struct/StructMember.hpp | 360 ++++++++++---------- include/xo/reflect/struct/StructTdx.hpp | 2 + include/xo/reflect/vector/VectorTdx.hpp | 19 +- src/reflect/TypeDescrExtra.cpp | 4 +- src/reflect/atomic/AtomicTdx.cpp | 1 + src/reflect/struct/StructTdx.cpp | 76 ++--- 12 files changed, 479 insertions(+), 379 deletions(-) create mode 100644 include/xo/reflect/reflect_struct.hpp diff --git a/include/xo/reflect/StructReflector.hpp b/include/xo/reflect/StructReflector.hpp index 750b8669..220990a5 100644 --- a/include/xo/reflect/StructReflector.hpp +++ b/include/xo/reflect/StructReflector.hpp @@ -9,153 +9,154 @@ #include namespace xo { - namespace reflect { - template - class SelfTagger {}; + namespace reflect { + template + class SelfTagger {}; - template - struct SelfTagger { - static TaggedPtr self_tp(void * object) { - return (reinterpret_cast(object))->self_tp(); - } - }; + template + struct SelfTagger { + static TaggedPtr self_tp(void * object) { + return (reinterpret_cast(object))->self_tp(); + } + }; - template - struct SelfTagger { - static TaggedPtr self_tp(void * /*object*/) { assert(false); return TaggedPtr::universal_null(); } - }; + template + struct SelfTagger { + static TaggedPtr self_tp(void * /*object*/) { assert(false); return TaggedPtr::universal_null(); } + }; - /* RAII pattern for reflecting a struct. + /* RAII pattern for reflecting a struct. + * + * Use: + * struct Foo { int x_; double y_; }; + * + * StructReflector 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 + class StructReflector { + public: + using struct_t = StructT; + + public: + StructReflector() : td_{EstablishTypeDescr::establish()} {} + ~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 + void reflect_member(std::string const & member_name, + MemberT OwnerT::* member_addr) { + + auto accessor + (GeneralStructMemberAccessor::make(member_addr)); + + /* used to do this in GeneralStructMemberAccessor<> ctor, + * but that introduces #include cycle + */ + Reflect::require(); + + 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; + + /* if self-tagging, can use .self_tp() to get most-derived tagged pointer */ + auto to_self_tp_fn + = ([](void * object) + { + return SelfTagger::self_tp(object); + }); + + auto tdx = StructTdx::make(std::move(this->member_v_), + have_to_self_tp, + to_self_tp_fn); + + this->td_->assign_tdextra(std::move(tdx)); + } + } /*complete*/ + + template + void adopt_ancestors() { + assert(Reflect::is_reflected()); + + TypeDescr ancestor_td = Reflect::require(); + + /* 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()); + } + } /*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 member_v_; + }; /*StructReflector*/ + + template + bool StructReflector::s_reflected_flag = false; + } /*namespace reflect*/ + + /* e.g. + * struct Foo { int bar_; }; + * struct Bar : public Foo { .. }; * - * Use: - * struct Foo { int x_; double y_; }; - * - * StructReflector 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(); + * StructReflector sr; + * REFLECT_EXPLICIT_MEMBER(sr, "bar", &Foo::bar_); */ - template - class StructReflector { - public: - using struct_t = StructT; - - public: - StructReflector() : td_{EstablishTypeDescr::establish()} {} - ~StructReflector() { - this->require_complete(); - } - - bool is_complete() const { return s_reflected_flag; } - bool is_incomplete() const { return !s_reflected_flag; } - - template - void reflect_member(std::string const & member_name, - MemberT OwnerT::* member_addr) { - - auto accessor - (GeneralStructMemberAccessor::make(member_addr)); - - /* used to do this in GeneralStructMemberAccessor<> ctor, - * but that introduces #include cycle - */ - Reflect::require(); - - 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; - - /* if self-tagging, can use .self_tp() to get most-derived tagged pointer */ - auto to_self_tp_fn - = ([](void * object) - { - return SelfTagger::self_tp(object); - }); - - auto tdx = StructTdx::make(std::move(this->member_v_), - have_to_self_tp, - to_self_tp_fn); - - this->td_->assign_tdextra(std::move(tdx)); - } - } /*complete*/ - - template - void adopt_ancestors() { - assert(Reflect::is_reflected()); - - TypeDescr ancestor_td = Reflect::require(); - - /* 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()); - } - } /*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 member_v_; - }; /*StructReflector*/ - - template - bool StructReflector::s_reflected_flag = false; - } /*namespace reflect*/ - - /* e.g. - * struct Foo { int bar_; }; - * struct Bar : public Foo { .. }; - * - * StructReflector 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 sr; - * REFLECT_LITERAL_MEMBER(sr, bar_); - * - * then REFLECT_LITERAL_MEMBER() expands to something like: - * sr.reflect_member("bar_", &StructReflector::struct_t::bar_) - */ + /* e.g. + * struct Foo { int bar_; }; + * + * StructReflector sr; + * REFLECT_LITERAL_MEMBER(sr, bar_); + * + * then REFLECT_LITERAL_MEMBER() expands to something like: + * sr.reflect_member("bar_", &StructReflector::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 sr; - * REFLECT_MEMBER(sr, bar); // reflects Foo::bar_ as "bar" - */ + /* like REFLECT_LITERAL_MEMBER(), but append trailing underscore + * + * minor convenience, so we can write + * struct Foo { int bar_; }; + * + * StructReflector 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*/ diff --git a/include/xo/reflect/TypeDescr.hpp b/include/xo/reflect/TypeDescr.hpp index 31c58063..034f9e1c 100644 --- a/include/xo/reflect/TypeDescr.hpp +++ b/include/xo/reflect/TypeDescr.hpp @@ -305,6 +305,7 @@ namespace xo { * .n_child() reports #of instance variables (that have been reflected) */ uint32_t n_child(void * object) const { return this->tdextra_->n_child(object); } + /** in some circumstances can use this with object=nullptr **/ TaggedPtr child_tp(uint32_t i, void * object) const; /* require: diff --git a/include/xo/reflect/TypeDescrExtra.hpp b/include/xo/reflect/TypeDescrExtra.hpp index bef9463e..68291654 100644 --- a/include/xo/reflect/TypeDescrExtra.hpp +++ b/include/xo/reflect/TypeDescrExtra.hpp @@ -54,6 +54,11 @@ namespace xo { */ 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; virtual TaggedPtr child_tp(uint32_t i, void * object) const = 0; /* require: * .is_struct() diff --git a/include/xo/reflect/atomic/AtomicTdx.hpp b/include/xo/reflect/atomic/AtomicTdx.hpp index 98e10cfe..9bd5e567 100644 --- a/include/xo/reflect/atomic/AtomicTdx.hpp +++ b/include/xo/reflect/atomic/AtomicTdx.hpp @@ -7,31 +7,32 @@ #include namespace xo { - namespace reflect { - class TaggedPtr; + 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; + /* 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 make(); + static std::unique_ptr make(); - // ----- Inherited from TypeDescrExtra ----- + // ----- Inherited from TypeDescrExtra ----- - virtual Metatype metatype() const override { return Metatype::mt_atomic; } - virtual uint32_t n_child(void * /*object*/) const override { return 0; } - virtual TaggedPtr child_tp(uint32_t /*i*/, void * /*object*/) const override; - virtual std::string const & struct_member_name(uint32_t i) const override; - //virtual StructMember const * struct_member(uint32_t /*i*/) const override { return nullptr; } + 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 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*/ + private: + AtomicTdx() = default; + }; /*TypeDescrExtra*/ + } /*namespace reflect*/ } /*namespace xo*/ /* end AtomicTdx.hpp */ diff --git a/include/xo/reflect/function/FunctionTdx.hpp b/include/xo/reflect/function/FunctionTdx.hpp index 5d2f57c2..64b02da3 100644 --- a/include/xo/reflect/function/FunctionTdx.hpp +++ b/include/xo/reflect/function/FunctionTdx.hpp @@ -33,6 +33,7 @@ namespace xo { 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; const std::string & struct_member_name(uint32_t i) const override; diff --git a/include/xo/reflect/reflect_struct.hpp b/include/xo/reflect/reflect_struct.hpp new file mode 100644 index 00000000..8c0c843a --- /dev/null +++ b/include/xo/reflect/reflect_struct.hpp @@ -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 will separately + * have been specialized for T. + * See discussion in [reflect_struct_info.hpp] + * + * + **/ + template + 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 * p_sr) { + const auto & member_info + = reflect_struct_member().get(); + + p_sr->reflect_member(member_info.member_name_.c_str(), + member_info.member_addr_); + + /** reflect remaining members **/ + sr_member_helper::add_members_from(p_sr); + } + }; + + template + struct sr_member_helper { + /** base case -- all members have been refleccted **/ + static void add_members_from(StructReflector *) {} + }; + } /*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() instead + **/ + template + TypeDescr reflect_struct() { + StructReflector sr; + + if (sr.is_incomplete()) + detail::sr_member_helper::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 **/ diff --git a/include/xo/reflect/struct/StructMember.hpp b/include/xo/reflect/struct/StructMember.hpp index 3c7bab62..e7b76413 100644 --- a/include/xo/reflect/struct/StructMember.hpp +++ b/include/xo/reflect/struct/StructMember.hpp @@ -9,227 +9,227 @@ #include namespace xo { -namespace reflect { - class AbstractStructMemberAccessor { - public: - virtual ~AbstractStructMemberAccessor() = default; + 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 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 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(); - */ - virtual TypeDescr member_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(); + */ + virtual TypeDescr member_td() const = 0; - /* get address of a particular member, given parent address */ - virtual void * address(void * struct_addr) const = 0; + /* get address of a particular member, given parent address */ + virtual void * address(void * struct_addr) const = 0; - virtual std::unique_ptr clone() const = 0; - }; /*AbstractStructMemberAccessor*/ + virtual std::unique_ptr 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 - class GeneralStructMemberAccessor : public AbstractStructMemberAccessor { - public: - /* pointer to a OwnerT member of type MemberT */ - using Memptr = MemberT OwnerT::*; + /* 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 + 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()}, - memptr_{memptr} {} - GeneralStructMemberAccessor(GeneralStructMemberAccessor const & x) = default; - virtual ~GeneralStructMemberAccessor() = default; + public: + GeneralStructMemberAccessor(Memptr memptr) : member_td_{EstablishTypeDescr::establish()}, + memptr_{memptr} {} + GeneralStructMemberAccessor(GeneralStructMemberAccessor const & x) = default; + virtual ~GeneralStructMemberAccessor() = default; - static std::unique_ptr make(Memptr memptr) { - return std::unique_ptr(new GeneralStructMemberAccessor(memptr)); } + static std::unique_ptr make(Memptr memptr) { + return std::unique_ptr(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; + /* 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*/ + return &(owner_addr->*memptr_); + } /*address_impl*/ - // ----- Inherited from AbstractStructMemberAccessor ----- + // ----- 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 - */ + 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*/ + 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(); } + virtual TypeDescr struct_td() const override { return EstablishTypeDescr::establish(); } - virtual TypeDescr member_td() const override { return this->member_td_; } + virtual TypeDescr member_td() const override { return this->member_td_; } - virtual void * address(void * struct_addr) const override { - return this->address_impl(reinterpret_cast(struct_addr)); - } /*address*/ + virtual void * address(void * struct_addr) const override { + return this->address_impl(reinterpret_cast(struct_addr)); + } /*address*/ - virtual std::unique_ptr clone() const override { - return std::unique_ptr - (new GeneralStructMemberAccessor(*this)); - } /*clone*/ + virtual std::unique_ptr clone() const override { + return std::unique_ptr + (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*/ + 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::make(&Foo::x_); - * - * or equivalently: - * auto foo_x_access = GeneralStructMemberAccessor::make(&Foo::x_); - * auto bar_x_access = AncestorStructMemberAccessor::adopt(foo_x_access); - * - * can use the 2nd form to adopt accessors from an already-reflected ancestor class - * - * Require: - * - StructT -isa-> AncestorT - */ - template - class AncestorStructMemberAccessor : public AbstractStructMemberAccessor { - public: - AncestorStructMemberAccessor(std::unique_ptr ancestor_accessor) - : ancestor_accessor_{std::move(ancestor_accessor)} {} - AncestorStructMemberAccessor(AncestorStructMemberAccessor const & x) = default; - virtual ~AncestorStructMemberAccessor() = default; + /* 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::make(&Foo::x_); + * + * or equivalently: + * auto foo_x_access = GeneralStructMemberAccessor::make(&Foo::x_); + * auto bar_x_access = AncestorStructMemberAccessor::adopt(foo_x_access); + * + * can use the 2nd form to adopt accessors from an already-reflected ancestor class + * + * Require: + * - StructT -isa-> AncestorT + */ + template + class AncestorStructMemberAccessor : public AbstractStructMemberAccessor { + public: + AncestorStructMemberAccessor(std::unique_ptr ancestor_accessor) + : ancestor_accessor_{std::move(ancestor_accessor)} {} + AncestorStructMemberAccessor(AncestorStructMemberAccessor const & x) = default; + virtual ~AncestorStructMemberAccessor() = default; - static std::unique_ptr - adopt(std::unique_ptr ancestor_accessor) { - return std::unique_ptr - (new AncestorStructMemberAccessor(std::move(ancestor_accessor))); - } /*adopt*/ + static std::unique_ptr + adopt(std::unique_ptr ancestor_accessor) { + return std::unique_ptr + (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; + 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*/ + return this->ancestor_accessor_->address(ancestor_addr); + } /*address_impl*/ - // ----- inherited from AbstractStructMemberAccessor ----- + // ----- inherited from AbstractStructMemberAccessor ----- #ifdef OBSOLETE - virtual TaggedPtr member_tp(void * struct_addr) const override { - AncestorT * ancestor_addr = reinterpret_cast(struct_addr); + virtual TaggedPtr member_tp(void * struct_addr) const override { + AncestorT * ancestor_addr = reinterpret_cast(struct_addr); - return this->ancestor_accessor_->member_tp(ancestor_addr); - } /*member_tp*/ + return this->ancestor_accessor_->member_tp(ancestor_addr); + } /*member_tp*/ #endif - virtual TypeDescr struct_td() const override { return EstablishTypeDescr::establish(); } - virtual TypeDescr member_td() const override { return this->ancestor_accessor_->member_td(); } + virtual TypeDescr struct_td() const override { return EstablishTypeDescr::establish(); } + 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(struct_addr)); - } + virtual void * address(void * struct_addr) const override { + return this->address_impl(reinterpret_cast(struct_addr)); + } - virtual std::unique_ptr clone() const override { - return std::unique_ptr - (new AncestorStructMemberAccessor(std::move(this->ancestor_accessor_->clone()))); - } /*clone*/ + virtual std::unique_ptr clone() const override { + return std::unique_ptr + (new AncestorStructMemberAccessor(std::move(this->ancestor_accessor_->clone()))); + } /*clone*/ - private: - /* .ancestor_accessor fetches some particular member of AncestorT */ - std::unique_ptr ancestor_accessor_; - }; /*AncestorStructMemberAccessor*/ + private: + /* .ancestor_accessor fetches some particular member of AncestorT */ + std::unique_ptr 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 accessor) - : member_name_{name}, accessor_{std::move(accessor)} {} - StructMember(StructMember && x) - : member_name_{std::move(x.member_name_)}, - accessor_{std::move(x.accessor_)} {} + /* describes a member of a struct/class + * see [reflect/StructReflector.hpp] + */ + class StructMember { + public: + StructMember() = default; + StructMember(std::string const & name, + std::unique_ptr 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(); + static StructMember null(); - std::string const & member_name() const { return member_name_; } + 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); } + 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 - StructMember for_descendant() const { - assert(EstablishTypeDescr::establish() == this->get_struct_td()); + /* 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 + StructMember for_descendant() const { + assert(EstablishTypeDescr::establish() == this->get_struct_td()); - return StructMember(this->member_name(), - std::move(AncestorStructMemberAccessor::adopt - (std::move(this->accessor_->clone())))); - } /*for_descendant*/ + return StructMember(this->member_name(), + std::move(AncestorStructMemberAccessor::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; - } + 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 accessor_; - }; /*StructMember*/ -} /*namespace reflect*/ + 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 accessor_; + }; /*StructMember*/ + } /*namespace reflect*/ } /*namespace xo*/ /* end StructMember.hpp */ diff --git a/include/xo/reflect/struct/StructTdx.hpp b/include/xo/reflect/struct/StructTdx.hpp index 15eebe9b..0cd524d8 100644 --- a/include/xo/reflect/struct/StructTdx.hpp +++ b/include/xo/reflect/struct/StructTdx.hpp @@ -67,7 +67,9 @@ namespace xo { 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 std::string const & struct_member_name(uint32_t i) const override; virtual StructMember const * struct_member(uint32_t i) const override; diff --git a/include/xo/reflect/vector/VectorTdx.hpp b/include/xo/reflect/vector/VectorTdx.hpp index 84e414b4..dcdc20c1 100644 --- a/include/xo/reflect/vector/VectorTdx.hpp +++ b/include/xo/reflect/vector/VectorTdx.hpp @@ -17,10 +17,14 @@ namespace xo { /* named ctor idiom. create new instance for a vector type */ //static std::unique_ptr 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; /* (forbidden) */ virtual std::string const & struct_member_name(uint32_t i) const override; @@ -41,12 +45,16 @@ namespace xo { return std::unique_ptr(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(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(object); @@ -61,7 +69,10 @@ namespace xo { */ template - using StdArrayTdx = StlVectorTdx>; + class StdArrayTdx : public StlVectorTdx> { + virtual uint32_t n_child(void * /*object*/) const override { return N; } + virtual uint32_t n_child_fixed() const override { return N; } + }; /*StdArrayTdx*/ // ----- std::vector ----- @@ -77,12 +88,18 @@ namespace xo { return std::unique_ptr(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(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(object); diff --git a/src/reflect/TypeDescrExtra.cpp b/src/reflect/TypeDescrExtra.cpp index e53a216b..9e82fc8e 100644 --- a/src/reflect/TypeDescrExtra.cpp +++ b/src/reflect/TypeDescrExtra.cpp @@ -12,7 +12,7 @@ namespace xo { namespace reflect { TaggedPtr TypeDescrExtra::most_derived_self_tp(TypeDescrBase const * object_td, - void * object) const + void * object) const { return TaggedPtr(object_td, object); } /*most_derived_self_tp*/ @@ -24,7 +24,7 @@ namespace xo { static std::string s_null; return s_null; } /*struct_member_name*/ - + StructMember const * TypeDescrExtra::struct_member(uint32_t /*i*/) const { assert(false); diff --git a/src/reflect/atomic/AtomicTdx.cpp b/src/reflect/atomic/AtomicTdx.cpp index 2e04ab4d..86e5d5ba 100644 --- a/src/reflect/atomic/AtomicTdx.cpp +++ b/src/reflect/atomic/AtomicTdx.cpp @@ -2,6 +2,7 @@ #include "atomic/AtomicTdx.hpp" #include "TaggedPtr.hpp" +#include namespace xo { namespace reflect { diff --git a/src/reflect/struct/StructTdx.cpp b/src/reflect/struct/StructTdx.cpp index 593ad388..bf969724 100644 --- a/src/reflect/struct/StructTdx.cpp +++ b/src/reflect/struct/StructTdx.cpp @@ -3,53 +3,53 @@ #include "struct/StructTdx.hpp" namespace xo { - using std::uint32_t; + using std::uint32_t; - namespace reflect { - std::unique_ptr - StructTdx::make(std::vector member_v, - bool have_to_self_tp, - std::function to_self_tp) - { - return std::unique_ptr(new StructTdx(std::move(member_v), - have_to_self_tp, - std::move(to_self_tp))); - } /*make*/ + namespace reflect { + std::unique_ptr + StructTdx::make(std::vector member_v, + bool have_to_self_tp, + std::function to_self_tp) + { + return std::unique_ptr(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(); - } + TaggedPtr + StructTdx::child_tp(uint32_t i, void * object) const + { + if (i >= this->member_v_.size()) { + /* TODO: raise exception here? */ + return TaggedPtr::universal_null(); + } - StructMember const & member_info = this->member_v_[i]; + StructMember const & member_info = this->member_v_[i]; - return member_info.get_member_tp(object); + return member_info.get_member_tp(object); - } /*get_child*/ + } /*get_child*/ - std::string const & - StructTdx::struct_member_name(uint32_t i) const - { - StructMember const * sm = this->struct_member(i); + 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*/ + 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; - } + 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*/ + return &(this->member_v_[i]); + } /*struct_member*/ + } /*namespace reflect*/ } /*namespace xo*/ /* end StructTdx.cpp */ From 2b9aff3640c69d01f06d7b4f7447ba35a4b6dc9b Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 25 Jun 2024 19:37:16 -0400 Subject: [PATCH 100/111] xo-reflect: + TypeDescr:: n_child_fixed(), fixed_child_td(i) --- include/xo/reflect/TypeDescr.hpp | 13 ++++++++++++- include/xo/reflect/TypeDescrExtra.hpp | 6 ++++++ include/xo/reflect/atomic/AtomicTdx.hpp | 1 + include/xo/reflect/function/FunctionTdx.hpp | 1 + include/xo/reflect/struct/StructTdx.hpp | 1 + include/xo/reflect/vector/VectorTdx.hpp | 10 ++++++++++ src/reflect/atomic/AtomicTdx.cpp | 6 ++++++ src/reflect/function/FunctionTdx.cpp | 6 ++++++ src/reflect/struct/StructTdx.cpp | 14 +++++++++++++- 9 files changed, 56 insertions(+), 2 deletions(-) diff --git a/include/xo/reflect/TypeDescr.hpp b/include/xo/reflect/TypeDescr.hpp index 870d3561..a91befc8 100644 --- a/include/xo/reflect/TypeDescr.hpp +++ b/include/xo/reflect/TypeDescr.hpp @@ -305,7 +305,18 @@ namespace xo { * .n_child() reports #of instance variables (that have been reflected) */ uint32_t n_child(void * object) const { return this->tdextra_->n_child(object); } - /** in some circumstances can use this with object=nullptr **/ + /** 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: diff --git a/include/xo/reflect/TypeDescrExtra.hpp b/include/xo/reflect/TypeDescrExtra.hpp index 68291654..f53508ae 100644 --- a/include/xo/reflect/TypeDescrExtra.hpp +++ b/include/xo/reflect/TypeDescrExtra.hpp @@ -59,6 +59,12 @@ namespace xo { * 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() diff --git a/include/xo/reflect/atomic/AtomicTdx.hpp b/include/xo/reflect/atomic/AtomicTdx.hpp index 9bd5e567..d5068199 100644 --- a/include/xo/reflect/atomic/AtomicTdx.hpp +++ b/include/xo/reflect/atomic/AtomicTdx.hpp @@ -26,6 +26,7 @@ namespace xo { 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; } diff --git a/include/xo/reflect/function/FunctionTdx.hpp b/include/xo/reflect/function/FunctionTdx.hpp index 64b02da3..02f60558 100644 --- a/include/xo/reflect/function/FunctionTdx.hpp +++ b/include/xo/reflect/function/FunctionTdx.hpp @@ -35,6 +35,7 @@ namespace xo { 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_; } diff --git a/include/xo/reflect/struct/StructTdx.hpp b/include/xo/reflect/struct/StructTdx.hpp index 0cd524d8..57afe277 100644 --- a/include/xo/reflect/struct/StructTdx.hpp +++ b/include/xo/reflect/struct/StructTdx.hpp @@ -71,6 +71,7 @@ namespace xo { 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; diff --git a/include/xo/reflect/vector/VectorTdx.hpp b/include/xo/reflect/vector/VectorTdx.hpp index dcdc20c1..ebd318c2 100644 --- a/include/xo/reflect/vector/VectorTdx.hpp +++ b/include/xo/reflect/vector/VectorTdx.hpp @@ -26,6 +26,7 @@ namespace xo { 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*/ @@ -33,6 +34,7 @@ namespace xo { // ----- StlVectorTdx ----- /* require: + * - VectorT::value_type * - VectorT.size() * - VectorT[int] :: lvalue */ @@ -60,6 +62,10 @@ namespace xo { return establish_most_derived_tp(&((*vec)[i])); } /*child_tp*/ + + virtual TypeDescr fixed_child_td(uint32_t /*i*/) const override { + return EstablishTypeDescr::establish(); + } }; /*StlVectorTdx*/ // ----- std::array ----- @@ -105,6 +111,10 @@ namespace xo { return establish_most_derived_tp(&((*vec)[i])); } + + virtual TypeDescr fixed_child_td(uint32_t /*i*/) const override { + return EstablishTypeDescr::establish(); + } }; /*StdVectorTdx*/ } /*namespace reflect*/ diff --git a/src/reflect/atomic/AtomicTdx.cpp b/src/reflect/atomic/AtomicTdx.cpp index 86e5d5ba..bb7e1b8d 100644 --- a/src/reflect/atomic/AtomicTdx.cpp +++ b/src/reflect/atomic/AtomicTdx.cpp @@ -2,6 +2,7 @@ #include "atomic/AtomicTdx.hpp" #include "TaggedPtr.hpp" +#include "TypeDescr.hpp" #include namespace xo { @@ -16,6 +17,11 @@ namespace xo { 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); diff --git a/src/reflect/function/FunctionTdx.cpp b/src/reflect/function/FunctionTdx.cpp index 27c0f8fc..551cfef0 100644 --- a/src/reflect/function/FunctionTdx.cpp +++ b/src/reflect/function/FunctionTdx.cpp @@ -2,6 +2,7 @@ #include "function/FunctionTdx.hpp" #include "TaggedPtr.hpp" +#include "TypeDescr.hpp" namespace xo { namespace reflect { @@ -36,6 +37,11 @@ namespace xo { 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 { diff --git a/src/reflect/struct/StructTdx.cpp b/src/reflect/struct/StructTdx.cpp index bf969724..c9edc11b 100644 --- a/src/reflect/struct/StructTdx.cpp +++ b/src/reflect/struct/StructTdx.cpp @@ -1,6 +1,7 @@ /* @file StructTdx.cpp */ #include "struct/StructTdx.hpp" +#include "TypeDescr.hpp" namespace xo { using std::uint32_t; @@ -24,12 +25,23 @@ namespace xo { return TaggedPtr::universal_null(); } - StructMember const & member_info = this->member_v_[i]; + 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 { From 771ccdd30839a30c5bac547f9af21e079978dfb6 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 26 Jun 2024 00:39:58 -0400 Subject: [PATCH 101/111] xo-reflect: cosmetic: indenting --- include/xo/reflect/TaggedRcptr.hpp | 116 ++++++++++++++--------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/include/xo/reflect/TaggedRcptr.hpp b/include/xo/reflect/TaggedRcptr.hpp index 3e06af64..5d051070 100644 --- a/include/xo/reflect/TaggedRcptr.hpp +++ b/include/xo/reflect/TaggedRcptr.hpp @@ -11,78 +11,78 @@ #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; + 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()); - } + 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] */ + /* causes #include cycle, see [reflect/Reflect.hpp] */ #ifdef NOT_IN_USE - /* require: T --isa--> ref::Refcount */ - template - static TaggedRcptr make(T * x) { return TaggedRcptr(Reflect::require(), x); } + /* require: T --isa--> ref::Refcount */ + template + static TaggedRcptr make(T * x) { return TaggedRcptr(Reflect::require(), x); } #endif - Refcount * rc_address() const { - return reinterpret_cast(this->address()); - } /*rc_address*/ + Refcount * rc_address() const { + return reinterpret_cast(this->address()); + } /*rc_address*/ - TaggedRcptr & operator=(TaggedRcptr const & rhs) { - Refcount * x = rhs.rc_address(); - Refcount * old = this->rc_address(); + TaggedRcptr & operator=(TaggedRcptr const & rhs) { + Refcount * x = rhs.rc_address(); + Refcount * old = this->rc_address(); - TaggedPtr::operator=(rhs); + TaggedPtr::operator=(rhs); - if (x != old) { - intrusive_ptr_release(old); - intrusive_ptr_add_ref(x); - } + if (x != old) { + intrusive_ptr_release(old); + intrusive_ptr_add_ref(x); + } - return *this; - } /*operator=*/ + 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_); + 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=*/ + return *this; + } /*operator=*/ - void display(std::ostream & os) const; - std::string display_string() const; - }; /*TaggedRcptr*/ + 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<<*/ + inline std::ostream & operator<<(std::ostream & os, TaggedRcptr const & x) { + x.display(os); + return os; + } /*operator<<*/ - } /*namespace reflect*/ + } /*namespace reflect*/ } /*namespace xo*/ /* end TaggedRcptr.hpp */ From 2c51a1e1a0f8ff19b5a7fef72e171bd6ff11613b Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 26 Jun 2024 00:40:11 -0400 Subject: [PATCH 102/111] xo-reflect: promote std::string to always-reflect --- src/reflect/TypeDescr.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/reflect/TypeDescr.cpp b/src/reflect/TypeDescr.cpp index 9bb4bc39..4d9e3d50 100644 --- a/src/reflect/TypeDescr.cpp +++ b/src/reflect/TypeDescr.cpp @@ -314,6 +314,7 @@ namespace xo { Reflect::require(); Reflect::require(); Reflect::require(); + Reflect::require(); } /*ctor*/ TypeDescrTable From 58e143f1b1c18b902dc1741f1bbfc99d054bf295 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 26 Jun 2024 00:40:28 -0400 Subject: [PATCH 103/111] xo-reflect: + Object (cache TypeId for dispatching) --- include/xo/reflect/Object.hpp | 44 +++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 include/xo/reflect/Object.hpp diff --git a/include/xo/reflect/Object.hpp b/include/xo/reflect/Object.hpp new file mode 100644 index 00000000..8fb8c156 --- /dev/null +++ b/include/xo/reflect/Object.hpp @@ -0,0 +1,44 @@ +** @file Object.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "xo/reflect/SelfTagging.hpp" +//#include + +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: + using TypeId = xo::reflect::TypeId; + + 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 **/ From 908c3ae50b8a8c5f035e78772ba06ab62b5e39b0 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 26 Jun 2024 00:45:18 -0400 Subject: [PATCH 104/111] xo-reflect: bugfix: missing char in Object.hpp --- include/xo/reflect/Object.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/xo/reflect/Object.hpp b/include/xo/reflect/Object.hpp index 8fb8c156..fc7f1607 100644 --- a/include/xo/reflect/Object.hpp +++ b/include/xo/reflect/Object.hpp @@ -1,4 +1,4 @@ -** @file Object.hpp +/** @file Object.hpp * * Author: Roland Conybeare **/ From 1ddc3e6a6269d95d9e7456d5ca7402b5a42f1f3b Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 27 Jun 2024 17:46:08 -0400 Subject: [PATCH 105/111] xo-reflect: clang nit on FunctionTdxInfo --- include/xo/reflect/TypeDescr.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/xo/reflect/TypeDescr.hpp b/include/xo/reflect/TypeDescr.hpp index a91befc8..95d9364c 100644 --- a/include/xo/reflect/TypeDescr.hpp +++ b/include/xo/reflect/TypeDescr.hpp @@ -119,7 +119,8 @@ namespace xo { /* hashable contents of a FunctionTdx instance (without requiring decl of TypeDescrExtra), * for unique-ification of manually-constructed function types */ - struct FunctionTdxInfo { + class FunctionTdxInfo { + public: FunctionTdxInfo() = default; FunctionTdxInfo(TypeDescr retval_td, const std::vector & arg_td_v, From fc2b88da4b0336bc47d237554ba5da293ee954af Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 5 Aug 2024 14:37:35 -0400 Subject: [PATCH 106/111] xo-reflect: feat: capture dtor --- include/xo/reflect/EstablishTypeDescr.hpp | 3 +- include/xo/reflect/Object.hpp | 2 - include/xo/reflect/Reflect.hpp | 11 +++-- include/xo/reflect/StructReflector.hpp | 5 +- include/xo/reflect/TypeDescr.hpp | 58 ++++++++++++++++++++--- src/reflect/TypeDescr.cpp | 9 +++- utest/CMakeLists.txt | 4 -- 7 files changed, 71 insertions(+), 21 deletions(-) diff --git a/include/xo/reflect/EstablishTypeDescr.hpp b/include/xo/reflect/EstablishTypeDescr.hpp index 423345dc..0f18d84a 100644 --- a/include/xo/reflect/EstablishTypeDescr.hpp +++ b/include/xo/reflect/EstablishTypeDescr.hpp @@ -41,7 +41,8 @@ namespace xo { static TypeDescrW establish() { TypeDescrW td = TypeDescrBase::require(&typeid(T), std::string(type_name()), - nullptr); + nullptr /*tdextra*/, + nullptr /*invoker*/); #ifdef NOT_USING std::function to_self_tp; diff --git a/include/xo/reflect/Object.hpp b/include/xo/reflect/Object.hpp index fc7f1607..91a5cedd 100644 --- a/include/xo/reflect/Object.hpp +++ b/include/xo/reflect/Object.hpp @@ -21,8 +21,6 @@ namespace xo { **/ class Object : public reflect::SelfTagging { public: - using TypeId = xo::reflect::TypeId; - Object(TypeId type_id) : type_id_{type_id} {} private: diff --git a/include/xo/reflect/Reflect.hpp b/include/xo/reflect/Reflect.hpp index 23179c13..d4cfd34c 100644 --- a/include/xo/reflect/Reflect.hpp +++ b/include/xo/reflect/Reflect.hpp @@ -36,7 +36,7 @@ namespace xo { // ----- xo::ref::rp ----- template - class EstablishTdx> { + class EstablishTdx> { public: /* definition provide after decl for Reflect {} below */ static std::unique_ptr make(); @@ -182,9 +182,12 @@ namespace xo { } else { /* control comes here the first time require() runs */ + static detail::InvokerAux s_final_invoker; + auto final_tdx = EstablishTdx::make(); - retval_td->assign_tdextra(std::move(final_tdx)); + retval_td->assign_tdextra(&s_final_invoker, + std::move(final_tdx)); /* also need to require for each child */ } @@ -274,7 +277,7 @@ namespace xo { */ template std::unique_ptr - EstablishTdx>::make() { + EstablishTdx>::make() { /* need to ensure Object is property reflected. * * In practice must be a class type, since has to store refcount @@ -282,7 +285,7 @@ namespace xo { */ Reflect::require(); - return RefPointerTdx>::make(); + return RefPointerTdx>::make(); } /*make*/ // ----- std::array ----- diff --git a/include/xo/reflect/StructReflector.hpp b/include/xo/reflect/StructReflector.hpp index 220990a5..b443950f 100644 --- a/include/xo/reflect/StructReflector.hpp +++ b/include/xo/reflect/StructReflector.hpp @@ -80,11 +80,14 @@ namespace xo { return SelfTagger::self_tp(object); }); + static detail::InvokerAux s_final_invoker; + auto tdx = StructTdx::make(std::move(this->member_v_), have_to_self_tp, to_self_tp_fn); - this->td_->assign_tdextra(std::move(tdx)); + this->td_->assign_tdextra(&s_final_invoker, + std::move(tdx)); } } /*complete*/ diff --git a/include/xo/reflect/TypeDescr.hpp b/include/xo/reflect/TypeDescr.hpp index 95d9364c..53247ad0 100644 --- a/include/xo/reflect/TypeDescr.hpp +++ b/include/xo/reflect/TypeDescr.hpp @@ -88,6 +88,33 @@ namespace xo { /* 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::dtor(p); + **/ + template + struct InvokerAux : public Invoker { + virtual void dtor(void * addr) const override { + T * obj = static_cast(addr); + + obj->~T(); + } + }; + + template<> + struct InvokerAux : public Invoker { + virtual void dtor(void *) const override {} + }; + } /*namespace detail*/ } /*namespace reflect*/ } /*namespace xo*/ @@ -191,6 +218,7 @@ namespace xo { */ static TypeDescrW require(const std::type_info * tinfo, const std::string & canonical_name, + detail::Invoker * invoker, std::unique_ptr tdextra); /** Create type-description for function from input ingredients. **/ @@ -364,16 +392,21 @@ namespace xo { /* call this once to attach extended type information to a type-description * (e.g. description of struct members for a record type) */ - void assign_tdextra(std::unique_ptr tdx) { + void assign_tdextra(detail::Invoker * invoker, + std::unique_ptr 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 tdextra); + std::unique_ptr tdextra, + detail::Invoker * invoker); void assign_native_tinfo(const std::type_info * tinfo) { assert(!native_typeinfo_); @@ -429,8 +462,9 @@ namespace xo { static std::unordered_map s_function_type_map; private: - /* unique id# for this type */ + /** 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. @@ -438,6 +472,7 @@ namespace xo { * see Lambda.cpp in xo-expression. **/ std::type_info const * native_typeinfo_ = nullptr; + /** canonical name for this type (see demangle.hpp for type_name()) * e.g. * xo::option::Px2 @@ -448,19 +483,28 @@ namespace xo { * 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 + + /** set to true once final value for .tdextra is established * intially all TypeDescr objects will use AtomicTdx for .tdextra * Reflect::require() upgrades .tdextra for particular types. * When that procedure makes a decision for a type T, * .complete_flag will be set to true for the corresponding TypeDescrBase instance - */ + **/ bool complete_flag_ = false; - /* additional type information that either: + + /** 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, @@ -469,7 +513,7 @@ namespace xo { * 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 tdextra_; }; /*TypeDescrBase*/ diff --git a/src/reflect/TypeDescr.cpp b/src/reflect/TypeDescr.cpp index 4d9e3d50..4d048015 100644 --- a/src/reflect/TypeDescr.cpp +++ b/src/reflect/TypeDescr.cpp @@ -54,6 +54,7 @@ namespace xo { TypeDescrW TypeDescrBase::require(const std::type_info * native_tinfo, const std::string & canonical_name, + detail::Invoker * invoker, std::unique_ptr tdextra) { if (native_tinfo) { @@ -152,7 +153,8 @@ namespace xo { auto new_td = new TypeDescrBase(new_td_id, native_tinfo, canonical_name, - std::move(tdextra)); + std::move(tdextra), + invoker); new_slot.reset(new_td); @@ -178,6 +180,7 @@ namespace xo { 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*/ @@ -240,11 +243,13 @@ namespace xo { TypeDescrBase::TypeDescrBase(TypeId id, const std::type_info * native_tinfo, const std::string & canonical_name, - std::unique_ptr tdextra) + std::unique_ptr 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)} { } diff --git a/utest/CMakeLists.txt b/utest/CMakeLists.txt index edc997ea..535907a5 100644 --- a/utest/CMakeLists.txt +++ b/utest/CMakeLists.txt @@ -9,10 +9,6 @@ set(SELF_SOURCE_FILES FunctionTdx.test.cpp) xo_add_utest_executable(${SELF_EXECUTABLE_NAME} ${SELF_SOURCE_FILES}) - -# ---------------------------------------------------------------- -# deps: indentlog, .. - xo_self_dependency(${SELF_EXECUTABLE_NAME} reflect) xo_external_target_dependency(${SELF_EXECUTABLE_NAME} Catch2 Catch2::Catch2) From 8b631bbcc1467abbf9de5c48a140bc81181dc68d Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 5 Aug 2024 14:37:54 -0400 Subject: [PATCH 107/111] xo-reflect: tidy: formatting in utest --- utest/VectorTdx.test.cpp | 262 +++++++++++++++++++-------------------- 1 file changed, 131 insertions(+), 131 deletions(-) diff --git a/utest/VectorTdx.test.cpp b/utest/VectorTdx.test.cpp index 00f30a48..fe4e58a4 100644 --- a/utest/VectorTdx.test.cpp +++ b/utest/VectorTdx.test.cpp @@ -7,175 +7,175 @@ #include namespace xo { - using xo::reflect::Reflect; - using xo::reflect::TaggedPtr; - using xo::reflect::TypeDescr; - using xo::reflect::Metatype; + 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 v; + namespace ut { + TEST_CASE("std-vector-reflect-empty", "[reflect]") { + std::vector v; - TaggedPtr tp = Reflect::make_tp(&v); - //TypeDescr td = Reflect::require>(); + TaggedPtr tp = Reflect::make_tp(&v); + //TypeDescr td = Reflect::require>(); - REQUIRE(Reflect::is_reflected>() == true); + REQUIRE(Reflect::is_reflected>() == 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>() == &v); - REQUIRE(tp.n_child() == 0); /*since empty vector*/ - // REQUIRE(tp.child_td(0) == ... - } /*TEST_CASE(std-vector-reflect-empty)*/ + 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>() == &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 v = { 1.123 }; + TEST_CASE("std-vector-reflect-one", "[reflect]") { + std::vector v = { 1.123 }; - TaggedPtr tp = Reflect::make_tp(&v); + TaggedPtr tp = Reflect::make_tp(&v); - REQUIRE(Reflect::is_reflected>() == true); + REQUIRE(Reflect::is_reflected>() == 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>() == &v); - REQUIRE(tp.n_child() == 1); + 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>() == &v); + REQUIRE(tp.n_child() == 1); - TaggedPtr tp0 = tp.get_child(0); + 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() == &(v[0])); - REQUIRE(tp0.n_child() == 0); - } /*TEST_CASE(std-vector-reflect-one)*/ + 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() == &(v[0])); + REQUIRE(tp0.n_child() == 0); + } /*TEST_CASE(std-vector-reflect-one)*/ - TEST_CASE("std-vector-reflect-two", "[reflect]") { - std::vector v = { 1.123, 2.234 }; + TEST_CASE("std-vector-reflect-two", "[reflect]") { + std::vector v = { 1.123, 2.234 }; - TaggedPtr tp = Reflect::make_tp(&v); + TaggedPtr tp = Reflect::make_tp(&v); - REQUIRE(Reflect::is_reflected>() == true); + REQUIRE(Reflect::is_reflected>() == 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>() == &v); - REQUIRE(tp.n_child() == 2); + 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>() == &v); + REQUIRE(tp.n_child() == 2); - TaggedPtr tp0 = tp.get_child(0); + 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() == &(v[0])); - REQUIRE(tp0.n_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() == &(v[0])); + REQUIRE(tp0.n_child() == 0); - TaggedPtr tp1 = tp.get_child(1); + 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() == &(v[1])); - REQUIRE(tp1.n_child() == 0); - } /*TEST(std-vector-reflect-two)*/ + 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() == &(v[1])); + REQUIRE(tp1.n_child() == 0); + } /*TEST(std-vector-reflect-two)*/ - // ----- std::array ----- + // ----- std::array ----- - TEST_CASE("std-array-reflect-empty", "[reflect]") { - std::array v; + TEST_CASE("std-array-reflect-empty", "[reflect]") { + std::array v; - TaggedPtr tp = Reflect::make_tp(&v); - //TypeDescr td = Reflect::require>(); + TaggedPtr tp = Reflect::make_tp(&v); + //TypeDescr td = Reflect::require>(); - REQUIRE(Reflect::is_reflected>() == true); + REQUIRE(Reflect::is_reflected>() == 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>() == &v); - REQUIRE(tp.n_child() == 0); /*since empty vector*/ - // REQUIRE(tp.child_td(0) == ... - } /*TEST_CASE(std-array-reflect-empty)*/ + 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>() == &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 v = { 1.123 }; + TEST_CASE("std-array-reflect-one", "[reflect]") { + std::array v = { 1.123 }; - TaggedPtr tp = Reflect::make_tp(&v); + TaggedPtr tp = Reflect::make_tp(&v); - REQUIRE(Reflect::is_reflected>() == true); + REQUIRE(Reflect::is_reflected>() == 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>() == &v); - REQUIRE(tp.n_child() == 1); + 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>() == &v); + REQUIRE(tp.n_child() == 1); - TaggedPtr tp0 = tp.get_child(0); + 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() == &(v[0])); - REQUIRE(tp0.n_child() == 0); - } /*TEST_CASE(std-array-reflect-one)*/ + 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() == &(v[0])); + REQUIRE(tp0.n_child() == 0); + } /*TEST_CASE(std-array-reflect-one)*/ - TEST_CASE("std-array-reflect-two", "[reflect]") { - std::array v = { 1.123, 2.234 }; + TEST_CASE("std-array-reflect-two", "[reflect]") { + std::array v = { 1.123, 2.234 }; - TaggedPtr tp = Reflect::make_tp(&v); + TaggedPtr tp = Reflect::make_tp(&v); - REQUIRE(Reflect::is_reflected>() == true); + REQUIRE(Reflect::is_reflected>() == 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>() == &v); - REQUIRE(tp.n_child() == 2); + 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>() == &v); + REQUIRE(tp.n_child() == 2); - TaggedPtr tp0 = tp.get_child(0); + 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() == &(v[0])); - REQUIRE(tp0.n_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() == &(v[0])); + REQUIRE(tp0.n_child() == 0); - TaggedPtr tp1 = tp.get_child(1); + 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() == &(v[1])); - REQUIRE(tp1.n_child() == 0); - } /*TEST(std-array-reflect-two)*/ + 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() == &(v[1])); + REQUIRE(tp1.n_child() == 0); + } /*TEST(std-array-reflect-two)*/ - } /*namespace ut*/ + } /*namespace ut*/ } /*namespace xo*/ /* end VectorTdx.test.cpp */ From b1c3dc80b15af53989a78f3454f248f20d5beb31 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 19 Aug 2024 18:50:35 -0400 Subject: [PATCH 108/111] xo-reflect: + invoker feature --- include/xo/reflect/Reflect.hpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/include/xo/reflect/Reflect.hpp b/include/xo/reflect/Reflect.hpp index d4cfd34c..f75c3e44 100644 --- a/include/xo/reflect/Reflect.hpp +++ b/include/xo/reflect/Reflect.hpp @@ -182,11 +182,9 @@ namespace xo { } else { /* control comes here the first time require() runs */ - static detail::InvokerAux s_final_invoker; - auto final_tdx = EstablishTdx::make(); - retval_td->assign_tdextra(&s_final_invoker, + retval_td->assign_tdextra(Reflect::get_final_invoker(), std::move(final_tdx)); /* also need to require for each child */ @@ -222,7 +220,8 @@ namespace xo { auto final_tdx = EstablishFunctionTdx::make(); - retval_td->assign_tdextra(std::move(final_tdx)); + retval_td->assign_tdextra(Reflect::get_final_invoker(), + std::move(final_tdx)); /* also need to require for each child */ } @@ -254,6 +253,16 @@ namespace xo { template static TaggedRcptr make_rctp(T * x) { return TaggedPtrMaker::make_rctp(x); } + + private: + + template + static detail::InvokerAux * get_final_invoker() { + static detail::InvokerAux s_final_invoker; + + return &s_final_invoker; + } + }; /*Reflect*/ // ----- MakeTagged ----- From 4179196e219521c766a087cc29dfe9b55bb9ec54 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 27 Aug 2024 16:30:08 -0400 Subject: [PATCH 109/111] xo-reflect: nit: README typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4e43dfd8..d39421b6 100644 --- a/README.md +++ b/README.md @@ -51,5 +51,5 @@ $ 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 +$ ln -s .build/compile_commands.json # lsp will look for compile_commands.json in the root of the source tree ``` From 41f5ee3f8890e16236fcfb857385c1b4a7017440 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sat, 14 Sep 2024 14:40:04 -0500 Subject: [PATCH 110/111] xo-reflect: bugfix: missing PointerTdx.n_child_fixed() --- CMakeLists.txt | 2 ++ include/xo/reflect/pointer/PointerTdx.hpp | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c2e8b74d..be7c1d7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,3 +25,5 @@ add_subdirectory(utest) # provide find_package() support xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) + +# end CMakeLists.txt diff --git a/include/xo/reflect/pointer/PointerTdx.hpp b/include/xo/reflect/pointer/PointerTdx.hpp index 037d3ee4..5e59a9d9 100644 --- a/include/xo/reflect/pointer/PointerTdx.hpp +++ b/include/xo/reflect/pointer/PointerTdx.hpp @@ -23,6 +23,12 @@ namespace xo { 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; From 5dce303fe10ce05c0b3af2a922694f8d64208ba2 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sat, 14 Sep 2024 18:26:10 -0500 Subject: [PATCH 111/111] xo-reflect: bugfix: missing RefPointerTdx.fixed_child_td() --- include/xo/reflect/pointer/PointerTdx.hpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/xo/reflect/pointer/PointerTdx.hpp b/include/xo/reflect/pointer/PointerTdx.hpp index 5e59a9d9..4484c6ed 100644 --- a/include/xo/reflect/pointer/PointerTdx.hpp +++ b/include/xo/reflect/pointer/PointerTdx.hpp @@ -28,7 +28,6 @@ namespace xo { * 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; @@ -36,7 +35,7 @@ namespace xo { // ----- RefPointerTdx ----- - /* xo::ref::intrusive_ptr for some T */ + /* Pointer = xo::ref::intrusive_ptr for some T */ template class RefPointerTdx : public PointerTdx { public: @@ -58,6 +57,10 @@ namespace xo { return 0; } /*n_child*/ + virtual TypeDescrBase * fixed_child_td(uint32_t /*i*/) const override { + return EstablishTypeDescr::establish(); + } + virtual TaggedPtr child_tp(uint32_t i, void * object) const override { using xo::tostr; using xo::xtag;