From 37ff6c2b019849bed606a7e99acab1078ea85b28 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 24 Jun 2024 22:25:45 -0400 Subject: [PATCH] initial commit --- .gitignore | 8 + CMakeLists.txt | 46 +++++ LICENSE | 29 +++ cmake/xo-bootstrap-macros.cmake | 33 ++++ cmake/xo_reflectutilConfig.cmake.in | 17 ++ .../xo/reflectutil/reflect_struct_info.hpp | 175 ++++++++++++++++++ 6 files changed, 308 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 cmake/xo-bootstrap-macros.cmake create mode 100644 cmake/xo_reflectutilConfig.cmake.in create mode 100644 include/xo/reflectutil/reflect_struct_info.hpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f77977 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +# emacs project control file +.projectile +# clangd working space (see emacs+lsp) +.cache +# typical cmake build directory (source-tree-nephew) +.build* +# symlink to builddir/compile_commands.json; should be set manually in dev sandbox +compile_commands.json diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a231f5d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,46 @@ +# xo-reflectutil/CMakeLists.txt + +cmake_minimum_required(VERSION 3.10) + +project(xo_reflectutil VERSION 1.0) +enable_language(CXX) + +include(GNUInstallDirs) +include(cmake/xo-bootstrap-macros.cmake) + +xo_cxx_toplevel_options3() + +# ---------------------------------------------------------------- +# c++ settings + +# one-time project-specific c++ flags. usually empty +set(PROJECT_CXX_FLAGS "") +#set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2") +add_definitions(${PROJECT_CXX_FLAGS}) + +# ---------------------------------------------------------------- + +#add_subdirectory(example) +#add_subdirectory(utest) + +# ---------------------------------------------------------------- +# provide find_package() support for projects using this library + +set(SELF_LIB xo_reflectutil) +xo_add_headeronly_library(${SELF_LIB}) +xo_install_library4(${SELF_LIB} ${PROJECT_NAME}Targets) +xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) + +# ---------------------------------------------------------------- +# docs targets depend on all the other library/utest targets +# +#add_subdirectory(docs) + +# ---------------------------------------------------------------- +# dependencies + +xo_headeronly_dependency(${SELF_LIB} xo_flatstring) +#xo_headeronly_dependency(${SELF_LIB} randomgen) +# etc.. + +# end CMakeLists.txt diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cae3cb5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,29 @@ +Copyright (c) 2024 Roland Conybeare , All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Please also refer to the file .github/CONTRIBUTING.md, which clarifies licensing of +external contributions to this project including patches, pull requests, etc. diff --git a/cmake/xo-bootstrap-macros.cmake b/cmake/xo-bootstrap-macros.cmake new file mode 100644 index 0000000..2cf387e --- /dev/null +++ b/cmake/xo-bootstrap-macros.cmake @@ -0,0 +1,33 @@ +# ---------------------------------------------------------------- +# 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 (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL "prefix")) + message(FATAL "could not find xo-cmake-config executable") +endif() + +if (NOT XO_SUBMODULE_BUILD) + if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL prefix)) + # default to typical install location for xo-project-macros + execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --cmake-module-path OUTPUT_VARIABLE CMAKE_MODULE_PATH) + message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") + endif() +endif() + +# needs to have been installed somewhere on CMAKE_MODULE_PATH, +# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX) +# +include(xo_macros/xo_cxx) + +xo_cxx_bootstrap_message() diff --git a/cmake/xo_reflectutilConfig.cmake.in b/cmake/xo_reflectutilConfig.cmake.in new file mode 100644 index 0000000..b7a5a0a --- /dev/null +++ b/cmake/xo_reflectutilConfig.cmake.in @@ -0,0 +1,17 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) + +# note: changes to find_dependency() calls here +# must coordinate with xo_dependency() calls +# in xo-reactor/src/reactor/CMakeLists.txt +# +find_dependency(xo_flatstring) +#find_dependency(subsys) +#find_dependency(Eigen3) +#find_dependency(webutil) +#find_dependency(printjson) +#find_dependency(callback) + +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") +check_required_components("@PROJECT_NAME@") diff --git a/include/xo/reflectutil/reflect_struct_info.hpp b/include/xo/reflectutil/reflect_struct_info.hpp new file mode 100644 index 0000000..9831308 --- /dev/null +++ b/include/xo/reflectutil/reflect_struct_info.hpp @@ -0,0 +1,175 @@ +/** @file reflect_struct_info.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include + +namespace xo { + namespace reflect { + /** Instructions for reflecting a struct: + * + * #include "xo/reflectutil/reflect_struct_info.hpp" // this file + * + * namespace xo { + * namespace reflect { + * // replacing X with the number of member variables in foo: + * REFLECT_BASE_STRUCT_INFO(some::ns::foo, X); + * + * // for each member {member_0, member_1, ...} of foo, + * // in the same order as they appear in class/struct decl: + * REFLECT_STRUCT_MEMBER_INFO(some::ns::foo, 0, member(0)); + * REFLECT_STRUCT_MEMBER_INFO(some::ns::foo, 1, member(1); + * .. + * // (last member will have index number X-1) + * } + * } + * + * These decls can appear anywhere after decl for some::ns::foo; + * they must be made in the global namespace + * (so that specializations appear in the xo::reflect namespace) + * + * Finally to permanently capture struct ingredients, + * this statement must run at least once: + * + * xo::reflect::reflect_struct(); + * + **/ + +#define REFLECT_BASE_STRUCT_INFO_TBODY(qualified_struct_name, member_count) \ + struct reflect_struct_traits { \ + static constexpr std::size_t n_members = member_count; \ + static constexpr std::size_t n_parents = 0; \ + } + +#define REFLECT_BASE_STRUCT_INFO(qualified_struct_name, member_count) \ + template<> \ + REFLECT_BASE_STRUCT_INFO_TBODY(qualified_struct_name, member_count) + +#define REFLECT_STRUCT_MEMBER_INFO_TBODY(qualified_struct_name, member_ix, member_name) \ + struct reflect_struct_member { \ + constexpr auto get() const { \ + return reflect_struct_member_aux(xo::flatstring(#member_name), \ + &qualified_struct_name::member_name##_); \ + } \ + }; + +#define REFLECT_STRUCT_MEMBER_INFO(qualified_struct_name, member_ix, member_name) \ + template <> \ + REFLECT_STRUCT_MEMBER_INFO_TBODY(qualified_struct_name, member_ix, member_name) + + /** @class reflect_struct_traits + * + * @brief header-only ingredients for reflecting a struct + * + * A class/struct T to be reflected should provide specializations + * of + * - xo::reflect::reflect_struct_traits<> + * - xo::reflect::reflect_struct_member<> + * - xo::reflect::reflect_struct_parent<> [aspirational, not yet] + * + * For example: + * + * @code + * + * namespace foo { + * struct mycomplex { double real_, double imag_; }; + * } + * + * namespace xo { + * namespace reflect { + * template <> + * struct reflect_struct_traits { + * + * // number of member variables + * static constexpr std::size_t n_members = 2; + * + * // number of parent structs + * // (i.e. directly inherited structs) + * static constexpr std::size_t n_parents = 0; + * }; + * } + * } + * + * // or use convenience macro (must be in global namespace) + * REFLECT_BASE_STRUCT_INFO(foo::mycomplex, 2); + * + * @endcode + **/ + template + struct reflect_struct_traits; + + /** @class reflect_struct_member + * @brief reflection ingredients for a particular struct member + * + * A class/struct T to be reflected should provide specializations + * of + * - xo::reflect::reflect_struct_traits<> + * - xo::reflect::reflect_struct_member<> + * - xo::reflect::reflect_struct_parent<> [aspirational, not yet] + * + * For example: + * + * @code + * + * namespace foo { + * struct mycomplex { double real_, double imag_; }; + * } + * + * namespace xo { + * namespace reflect { + * // reflect foo::mycomplex::real_ + * template <> + * struct reflect_struct_member { + * constexpr auto operator()() const { return reflect_struct_member_aux(xo::flatstring("real"), &foo::mycomplex::real_); } + * }; + * + * // reflect foo::mycomplex::imag_ + * template <> + * struct reflect_struct_member { + * constexpr auto operator()() const { return reflect_struct_member_aux(xo::flatstring("imag"), &foo::mycomplex::imag_); } + * }; + * } + * } + * + * // or use convenience macro (must be invoked from global namespace) + * REFLECT_STRUCT_MEMBER_INFO(foo::mycomplex, 0, real); + * REFLECT_STRUCT_MEMBER_INFO(foo::mycomplex, 1, imag); + * + * @endcode + **/ + template + struct reflect_struct_member; + + // ---------------------------------------------------------------- + + template + struct reflect_struct_member_info { + //using struct_type = StructT; + using owner_type = OwnerT; + using member_type = MemberT; + using member_name_type = MemberNameT; + + constexpr reflect_struct_member_info(const MemberNameT & member_name, + MemberT OwnerT::* member_addr) + : member_name_{member_name}, member_addr_{member_addr} {} + + MemberNameT member_name_; + MemberT OwnerT::* member_addr_; + }; + + // ---------------------------------------------------------------- + + template + constexpr auto reflect_struct_member_aux(const MemberNameT & member_name, + MemberT OwnerT:: * member_addr) { + return reflect_struct_member_info + (member_name, member_addr); + }; + + } /*namespace reflect*/ +} /*namespace xo*/ + +/** end reflect_struct_info.hpp **/