├── .gitmodules ├── .gitignore ├── cmake └── metacpp-config.cmake.in ├── test ├── test-other.hpp ├── example.h ├── CMakeLists.txt ├── include │ └── test │ │ └── example.h ├── loader.cpp ├── test.hpp ├── test.cpp └── example.cpp ├── ast ├── CMakeLists.txt ├── compile_info.cpp ├── clang.hpp └── parse.cpp ├── include └── metacpp │ ├── config.hpp.in │ ├── plugin.hpp │ ├── serial.hpp │ ├── ast.hpp │ └── refl.hpp ├── plugin ├── CMakeLists.txt └── plugin.cpp ├── CMakeLists.txt ├── refl ├── refl.cpp ├── CMakeLists.txt ├── tool.cpp └── make_meta.hpp ├── README.md └── LICENSE /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # in-tree builds 2 | build 3 | build-* 4 | docs 5 | 6 | # Desktop Environment related junk 7 | *.kate-swp 8 | 9 | # IDE/Compiler related junk 10 | .cache 11 | .kdev4 12 | *.kdev4 13 | CMakeLists.txt.user 14 | -------------------------------------------------------------------------------- /cmake/metacpp-config.cmake.in: -------------------------------------------------------------------------------- 1 | set(metacpp_VERSION @metacpp_VERSION_MAJOR@.@metacpp_VERSION_MINOR@.@metacpp_VERSION_PATCH@) 2 | 3 | @PACKAGE_INIT@ 4 | 5 | set_and_check(metacpp_INCLUDE_DIR "@CMAKE_INSTALL_INCLUDEDIR@") 6 | set_and_check(metacpp_SYSCONFIG_DIR "@CMAKE_INSTALL_SYSCONFDIR@") 7 | 8 | check_required_components(metacpp) 9 | -------------------------------------------------------------------------------- /test/test-other.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Meta C++ Tool and Library 3 | * Copyright (C) 2022 Keith Hammond 4 | * 5 | * This Source Code Form is subject to the terms of the Mozilla Public 6 | * License, v. 2.0. If a copy of the MPL was not distributed with this 7 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef METACPP_TEST_OTHER_HPP 11 | #define METACPP_TEST_OTHER_HPP 1 12 | 13 | #endif // !METACPP_TEST_OTHER_HPP 14 | -------------------------------------------------------------------------------- /test/example.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Meta C++ Tool and Library 3 | * Copyright (C) 2022 Keith Hammond 4 | * 5 | * This Source Code Form is subject to the terms of the Mozilla Public 6 | * License, v. 2.0. If a copy of the MPL was not distributed with this 7 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | namespace ns{ 17 | template 18 | using Vector = std::vector; 19 | } 20 | 21 | class [[my::attrib(1, "2", 3.0)]] example{ 22 | public: 23 | void method1(std::string_view s) const noexcept; 24 | void method1(const std::string &str) noexcept; 25 | 26 | std::tuple method2(); 27 | 28 | std::string_view member1; 29 | 30 | [[property]] 31 | ns::Vector member2; 32 | 33 | static float testVal() noexcept{ return 42.f; } 34 | }; 35 | 36 | enum class example_enum{ 37 | case_0 = 69, 38 | case_1 = 420, 39 | case_2 = 1337 40 | }; 41 | -------------------------------------------------------------------------------- /ast/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 4 | 5 | SET(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL) 6 | SET(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC) 7 | 8 | find_package(Clang CONFIG REQUIRED) 9 | 10 | add_library(metacpp-ast STATIC clang.hpp compile_info.cpp parse.cpp) 11 | add_library(metacpp::ast ALIAS metacpp-ast) 12 | add_library(metapp::ast ALIAS metacpp-ast) 13 | 14 | set_target_properties( 15 | metacpp-ast PROPERTIES 16 | C_STANDARD 17 17 | CXX_STANDARD 17 18 | ) 19 | 20 | target_compile_features(metacpp-ast PUBLIC c_std_17 cxx_std_17) 21 | 22 | target_link_libraries( 23 | metacpp-ast PUBLIC metacpp::headers fmt::fmt-header-only 24 | ) 25 | 26 | target_link_libraries( 27 | metacpp-ast PRIVATE libclang 28 | ) 29 | 30 | target_include_directories( 31 | metacpp-ast PRIVATE ${CLANG_INCLUDE_DIRS} 32 | ) 33 | 34 | if(METACPP_IPO_SUPPORTED) 35 | set_target_properties( 36 | metacpp-ast PROPERTIES INTERPROCEDURAL_OPTIMIZATION ON 37 | ) 38 | endif() 39 | -------------------------------------------------------------------------------- /include/metacpp/config.hpp.in: -------------------------------------------------------------------------------- 1 | /* 2 | * Meta C++ Tool and Library 3 | * Copyright (C) 2022 Keith Hammond 4 | * 5 | * This Source Code Form is subject to the terms of the Mozilla Public 6 | * License, v. 2.0. If a copy of the MPL was not distributed with this 7 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef METACPP_CONFIG_HPP 11 | #define METACPP_CONFIG_HPP 1 12 | 13 | #cmakedefine METACPP_REPO_BRANCH "@METACPP_REPO_BRANCH@" 14 | #cmakedefine METACPP_REPO_HASH "@METACPP_REPO_HASH@" 15 | 16 | #define METACPP_VERSION_MAJOR @METACPP_VERSION_MAJOR@ 17 | #define METACPP_VERSION_MINOR @METACPP_VERSION_MINOR@ 18 | #define METACPP_VERSION_PATCH @METACPP_VERSION_PATCH@ 19 | 20 | #define METACPP_VERSION_STR "@METACPP_VERSION_MAJOR@.@METACPP_VERSION_MINOR@.@METACPP_VERSION_PATCH@" 21 | 22 | #define METACPP_VERSION_GIT "@METACPP_REPO_BRANCH@@@METACPP_REPO_HASH@" 23 | 24 | #ifdef __GNUC__ 25 | #define REFLCPP_EXPORT_SYMBOL __attribute__((visibility ("default"))) 26 | #define REFLCPP_IMPORT_SYMBOL 27 | #else 28 | #define REFLCPP_EXPORT_SYMBOL __declspec(dllexport) 29 | #define REFLCPP_IMPORT_SYMBOL __declspec(dllimport) 30 | #endif 31 | 32 | #ifndef METACPP_NO_NAMESPACE_ALIAS 33 | 34 | #ifndef REFLCPP_IMPLEMENTATION 35 | #define REFLCPP_API REFLCPP_IMPORT_SYMBOL 36 | #else 37 | #define REFLCPP_API REFLCPP_EXPORT_SYMBOL 38 | #endif 39 | 40 | #ifndef METACPP_AST_NAMESPACE 41 | #define METACPP_AST_NAMESPACE ast 42 | #endif 43 | 44 | #ifndef METACPP_PLUGIN_NAMESPACE 45 | #define METACPP_PLUGIN_NAMESPACE plugin 46 | #endif 47 | 48 | #ifndef METACPP_SERIAL_NAMESPACE 49 | #define METACPP_SERIAL_NAMESPACE serial 50 | #endif 51 | 52 | #ifndef METACPP_REFL_NAMESPACE 53 | #define METACPP_REFL_NAMESPACE refl 54 | #endif 55 | 56 | #ifndef METACPP_META_NAMESPACE 57 | #define METACPP_META_NAMESPACE meta 58 | #endif 59 | 60 | #endif // !METACPP_NO_NAMESPACE_ALIAS 61 | 62 | #endif // !METACPP_CONFIG_HPP 63 | -------------------------------------------------------------------------------- /ast/compile_info.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Meta C++ Tool and Library 3 | * Copyright (C) 2022 Keith Hammond 4 | * 5 | * This Source Code Form is subject to the terms of the Mozilla Public 6 | * License, v. 2.0. If a copy of the MPL was not distributed with this 7 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #include "clang.hpp" 11 | 12 | namespace fs = std::filesystem; 13 | 14 | using namespace astpp; 15 | 16 | struct ast::compile_info::data{ 17 | data(const std::filesystem::path &build_dir) 18 | : db(build_dir){} 19 | 20 | clang::compilation_database db; 21 | }; 22 | 23 | compile_info::compile_info(const fs::path &build_dir) 24 | : impl(std::make_unique(build_dir)) 25 | {} 26 | 27 | compile_info::~compile_info(){} 28 | 29 | std::vector compile_info::all_options(const std::vector &add_args) const{ 30 | return impl->db.all_options(add_args); 31 | } 32 | 33 | std::vector compile_info::file_options(const std::filesystem::path &path, const std::vector &add_args) const{ 34 | return impl->db.file_options(path, add_args); 35 | } 36 | 37 | std::vector compile_info::all_include_dirs() const{ 38 | std::vector ret; 39 | 40 | const auto options = impl->db.all_options(); 41 | 42 | for(std::string_view opt : options){ 43 | if(opt.substr(0, 2) == "-I"){ 44 | ret.emplace_back(opt.substr(2)); 45 | } 46 | } 47 | 48 | std::sort(ret.begin(), ret.end()); 49 | ret.erase(std::unique(ret.begin(), ret.end()), ret.end()); 50 | 51 | return ret; 52 | } 53 | 54 | std::vector compile_info::file_include_dirs(const std::filesystem::path &path) const{ 55 | std::vector ret; 56 | 57 | const auto options = impl->db.file_options(path); 58 | 59 | for(std::string_view opt : options){ 60 | if(opt.substr(0, 2) == "-I"){ 61 | ret.emplace_back(opt.substr(2)); 62 | } 63 | } 64 | 65 | return ret; 66 | } 67 | -------------------------------------------------------------------------------- /include/metacpp/plugin.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Meta C++ Tool and Library 3 | * Copyright (C) 2022 Keith Hammond 4 | * 5 | * This Source Code Form is subject to the terms of the Mozilla Public 6 | * License, v. 2.0. If a copy of the MPL was not distributed with this 7 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef METACPP_PLUGIN_HPP 11 | #define METACPP_PLUGIN_HPP 1 12 | 13 | /** 14 | * @defgroup Plugin Plugin utilities 15 | * @{ 16 | */ 17 | 18 | #include "refl.hpp" 19 | 20 | #include 21 | #include 22 | 23 | namespace pluginpp{ 24 | struct function_info{ 25 | std::string_view name; 26 | reflpp::type_info result_type; 27 | std::vector param_types; 28 | }; 29 | 30 | /** 31 | * @brief Information about an executable/plugin. 32 | */ 33 | class library{ 34 | public: 35 | virtual ~library() = default; 36 | 37 | virtual const std::vector &symbols() const noexcept = 0; 38 | 39 | virtual const std::vector &exported_types() const noexcept = 0; 40 | virtual const std::vector &exported_functions() const noexcept = 0; 41 | 42 | virtual std::string demangle(const std::string &symbol_name) const noexcept = 0; 43 | 44 | virtual void *get_symbol(const std::string &name) const noexcept = 0; 45 | }; 46 | 47 | /** 48 | * @brief Get a list of plugins placed in the same folder as the executable. 49 | */ 50 | std::vector nearby_plugins(); 51 | 52 | /** 53 | * @brief Try to load a plugin. 54 | * @returns `nullptr` on error, handle to the loaded plugin on success 55 | */ 56 | const library *load(const std::filesystem::path &path); 57 | 58 | /** 59 | * @brief Get a reference to the running executable. 60 | */ 61 | const library *self(); 62 | } 63 | 64 | #ifndef METACPP_NO_NAMESPACE_ALIAS 65 | namespace METACPP_PLUGIN_NAMESPACE = pluginpp; 66 | #endif 67 | 68 | /** 69 | * @} 70 | */ 71 | 72 | #endif // !METACPP_PLUGIN_HPP 73 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 4 | 5 | enable_testing() 6 | 7 | add_plugin( 8 | plugin-test 9 | 10 | INCLUDE_DIRS 11 | include 12 | 13 | HEADERS 14 | test.hpp 15 | ) 16 | 17 | add_plugin( 18 | plugin-test-other 19 | 20 | INCLUDE_DIRS 21 | include 22 | 23 | HEADERS 24 | include/test/example.h 25 | 26 | LIBRARIES 27 | plugin-test 28 | ) 29 | 30 | add_executable(loader-test include/test/example.h loader.cpp) 31 | 32 | add_executable(ast-test test.hpp test.cpp) 33 | add_executable(meta-example example.h example.cpp) 34 | 35 | target_compile_features(ast-test PRIVATE cxx_std_20) 36 | target_compile_features(meta-example PRIVATE cxx_std_20) 37 | target_compile_features(loader-test PRIVATE cxx_std_20) 38 | 39 | target_compile_options(ast-test PRIVATE "-Wall") 40 | target_compile_options(meta-example PRIVATE "-Wall") 41 | target_compile_options(loader-test PRIVATE "-Wall") 42 | target_compile_options(plugin-test PRIVATE "-Wall") 43 | target_compile_options(plugin-test-other PRIVATE "-Wall") 44 | 45 | set_target_properties( 46 | ast-test meta-example loader-test PROPERTIES 47 | CXX_STANDARD 20 48 | CXX_STANDARD_REQUIRED ON 49 | ) 50 | 51 | target_include_directories(loader-test PRIVATE include) 52 | 53 | target_link_libraries(ast-test PUBLIC metacpp::ast metacpp::refl) 54 | target_link_libraries(loader-test PRIVATE fmt::fmt-header-only plugin-test) 55 | target_link_plugins(loader-test plugin-test-other) 56 | 57 | if(METACPP_IPO_SUPPORTED) 58 | set_target_properties( 59 | ast-test meta-example loader-test PROPERTIES 60 | INTERPROCEDURAL_OPTIMIZATION ON 61 | ) 62 | endif() 63 | 64 | target_reflect(plugin-test) 65 | target_reflect(plugin-test-other) 66 | target_reflect(loader-test) 67 | target_reflect(ast-test) 68 | target_reflect(meta-example) 69 | 70 | add_test( 71 | NAME tool-test 72 | COMMAND 73 | reflpp -o ${CMAKE_BINARY_DIR}/test ${CMAKE_BINARY_DIR} test/test.hpp 74 | ) 75 | -------------------------------------------------------------------------------- /include/metacpp/serial.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Meta C++ Tool and Library 3 | * Copyright (C) 2022 Keith Hammond 4 | * 5 | * This Source Code Form is subject to the terms of the Mozilla Public 6 | * License, v. 2.0. If a copy of the MPL was not distributed with this 7 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef METACPP_SERIAL_HPP 11 | #define METACPP_SERIAL_HPP 1 12 | 13 | /** 14 | * @defgroup Serial Serialization utilities 15 | * @warning These features are currently experimental. 16 | * @{ 17 | */ 18 | 19 | #include "meta.hpp" 20 | 21 | #include 22 | #include 23 | 24 | namespace serialpp{ 25 | namespace detail{ 26 | template 27 | struct to_json_helper; 28 | 29 | template 30 | struct to_json_helper>>{ 31 | static std::string invoke(const Class &cls){ 32 | std::ostringstream output; 33 | output << std::noshowpoint; 34 | 35 | output << "{\"" << metapp::type_name << "\":["; 36 | 37 | metapp::for_all_i>( 38 | [&]{ 39 | constexpr std::string_view name = Member::name; 40 | constexpr std::string_view type_name = meta::type_name; 41 | const auto &val = Member::get(cls); 42 | if constexpr(Idx != 0){ 43 | output << ","; 44 | } 45 | output << "{\"member\":{"; 46 | output << "\"name\":\""; 47 | output << name; 48 | output << "\",\"type\":\""; 49 | output << type_name; 50 | output << "\",\"value\":\""; 51 | output << val; 52 | output << "\"}}"; 53 | } 54 | ); 55 | 56 | output << "]}"; 57 | 58 | return output.str(); 59 | } 60 | }; 61 | } 62 | 63 | template 64 | std::string to_json(const T &val){ 65 | return detail::to_json_helper::invoke(val); 66 | } 67 | } 68 | 69 | #ifndef METACPP_NO_NAMESPACE_ALIAS 70 | namespace METACPP_SERIAL_NAMESPACE = serialpp; 71 | #endif 72 | 73 | /** 74 | * @} 75 | */ 76 | 77 | #endif // !METACPP_SERIAL_HPP 78 | -------------------------------------------------------------------------------- /test/include/test/example.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Meta C++ Tool and Library 3 | * Copyright (C) 2022 Keith Hammond 4 | * 5 | * This Source Code Form is subject to the terms of the Mozilla Public 6 | * License, v. 2.0. If a copy of the MPL was not distributed with this 7 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | #include 14 | 15 | #include "metacpp/meta.hpp" 16 | 17 | #include "test.meta.hpp" 18 | 19 | class [[my::attrib(1, "2", 3.0)]] example{ 20 | public: 21 | void method1(std::string_view s) const noexcept; 22 | void method1(const std::string &str) noexcept; 23 | 24 | std::tuple method2(); 25 | }; 26 | 27 | class example_test_derived: public TestTemplateClass{ 28 | public: 29 | explicit example_test_derived(int i) 30 | : m_str(std::to_string(i)) 31 | { 32 | set_value(m_str); 33 | } 34 | 35 | ~example_test_derived(){} 36 | 37 | private: 38 | std::string m_str; 39 | }; 40 | 41 | namespace ex{ 42 | namespace detail{ 43 | template 44 | class helper; 45 | 46 | template 47 | class helper>: public TestTemplateClass...{}; 48 | 49 | template 50 | class helper_helper: public helper{ 51 | template 52 | ex::detail::helper> rebind(){ 53 | return {}; 54 | } 55 | 56 | ex::detail::helper &self(){ return *this; } 57 | }; 58 | } 59 | 60 | template 61 | class example_test_helped: public detail::helper_helper>{ 62 | 63 | }; 64 | 65 | using example_helped_alias = example_test_helped; 66 | 67 | template 68 | using example_helped_tmpl_alias = example_test_helped; 69 | } 70 | 71 | class example_helped_instance: public ex::example_helped_alias{}; 72 | //class example_helped_instance1: public ex::example_helped_tmpl_alias{}; 73 | 74 | enum class example_enum{ 75 | case_0 = 69, 76 | case_1 = 420, 77 | case_2 = 1337 78 | }; 79 | 80 | inline example_enum example_fn(){ 81 | return example_enum::case_0; 82 | } 83 | -------------------------------------------------------------------------------- /test/loader.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Meta C++ Tool and Library 3 | * Copyright (C) 2022 Keith Hammond 4 | * 5 | * This Source Code Form is subject to the terms of the Mozilla Public 6 | * License, v. 2.0. If a copy of the MPL was not distributed with this 7 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #include 11 | 12 | #include "fmt/format.h" 13 | 14 | #include "metacpp/plugin.hpp" 15 | 16 | #include "test/example.meta.h" 17 | 18 | int main(int argc, char *argv[]){ 19 | auto plugs = plugin::nearby_plugins(); 20 | 21 | for(auto &&path : plugs){ 22 | plugin::load(path); 23 | } 24 | 25 | refl::class_info example_cls; 26 | assert(example_cls = refl::reflect_class("example")); 27 | 28 | assert( 29 | (refl::attribute(example_cls, "my::attrib") == std::vector{ "1", "\"2\"", "3.0" }) 30 | ); 31 | 32 | refl::class_info helped_cls; 33 | assert(helped_cls = refl::reflect_class("example_helped_instance")); 34 | 35 | auto instance = refl::value>(meta::type{}); 36 | 37 | assert(instance.as>()); 38 | 39 | assert(!refl::reflect_all_classes().empty()); 40 | 41 | refl::class_info derived_test_cls; 42 | assert(derived_test_cls = refl::reflect_class("example_test_derived")); 43 | assert(derived_test_cls->size() == sizeof(example_test_derived)); 44 | assert(derived_test_cls->alignment() == alignof(example_test_derived)); 45 | assert(derived_test_cls->num_bases() == 1); 46 | assert(derived_test_cls->base(0) == refl::reflect>()); 47 | 48 | auto derived_from_infos = refl::reflect_all_derived>(); 49 | assert(derived_from_infos.size() >= 1); 50 | assert(derived_from_infos[0] == derived_test_cls); 51 | 52 | { 53 | auto derived_instance = refl::value>(derived_test_cls, (int)1); 54 | assert(derived_instance.as>()); 55 | assert(derived_instance->value() == "1"); 56 | } 57 | 58 | refl::class_info derived_cls; 59 | assert(derived_cls = refl::reflect_class("TestDerived")); 60 | 61 | refl::class_info base_cls; 62 | assert(base_cls = refl::reflect_class("TestBase")); 63 | 64 | assert(refl::has_base(derived_cls, base_cls)); 65 | 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /plugin/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 4 | 5 | add_library(metacpp-plugin STATIC plugin.cpp) 6 | add_library(metacpp::plugin ALIAS metacpp-plugin) 7 | add_library(metapp::plugin ALIAS metacpp-plugin) 8 | 9 | target_link_libraries(metacpp-plugin PRIVATE metacpp::refl Boost::system ${CMAKE_DL_LIBS}) 10 | 11 | if(METACPP_IPO_SUPPORTED) 12 | set_target_properties( 13 | metacpp-plugin PROPERTIES INTERPROCEDURAL_OPTIMIZATION ON 14 | ) 15 | endif() 16 | 17 | function(add_plugin tgt) 18 | add_library(${tgt} SHARED) 19 | 20 | set_target_properties( 21 | ${tgt} PROPERTIES 22 | CXX_STANDARD 17 23 | CXX_STANDARD_REQUIRED ON 24 | POSITION_INDEPENDENT_CODE ON 25 | ) 26 | 27 | target_link_libraries(${tgt} PRIVATE metacpp::attribs) 28 | 29 | set(DO_REFLECT OFF) 30 | set(CUR_MODE "SOURCES") 31 | 32 | foreach(arg IN LISTS ARGN) 33 | if("${arg}" STREQUAL "REFLECT") 34 | set(DO_REFLECT ON) 35 | elseif("${arg}" STREQUAL "INCLUDE_DIRS") 36 | set(CUR_MODE "INCLUDE_DIRS") 37 | elseif("${arg}" STREQUAL "SOURCES") 38 | set(CUR_MODE "SOURCES") 39 | elseif("${arg}" STREQUAL "HEADERS") 40 | set(CUR_MODE "HEADERS") 41 | elseif("${arg}" STREQUAL "LIBRARIES") 42 | set(CUR_MODE "LIBRARIES") 43 | elseif("${CUR_MODE}" STREQUAL "INCLUDE_DIRS") 44 | target_include_directories(${tgt} PRIVATE ${arg}) 45 | elseif("${CUR_MODE}" STREQUAL "SOURCES") 46 | set_source_files_properties(${arg} PROPERTIES LANGUAGE CXX) 47 | target_sources(${tgt} PRIVATE ${arg}) 48 | elseif("${CUR_MODE}" STREQUAL "HEADERS") 49 | set_source_files_properties(${arg} PROPERTIES LANGUAGE CXX HEADER_FILE_ONLY TRUE) 50 | target_sources(${tgt} PRIVATE ${arg}) 51 | elseif("${CUR_MODE}" STREQUAL "LIBRARIES") 52 | target_link_libraries(${tgt} PRIVATE ${arg}) 53 | else() 54 | message(FATAL_ERROR "Unexpected argument to add_plugin: ${arg}") 55 | endif() 56 | endforeach() 57 | 58 | if(DO_REFLECT) 59 | target_reflect(${tgt}) 60 | endif() 61 | endfunction() 62 | 63 | function(target_link_plugins tgt) 64 | target_link_libraries(${tgt} PRIVATE metacpp::headers metacpp::plugin) 65 | 66 | add_custom_command( 67 | TARGET ${tgt} 68 | POST_BUILD 69 | COMMAND ${CMAKE_COMMAND} -E make_directory $ 70 | VERBATIM 71 | ) 72 | 73 | foreach(plugin IN LISTS ARGN) 74 | add_dependencies(${tgt} ${plugin}) 75 | 76 | add_custom_command( 77 | TARGET ${tgt} 78 | POST_BUILD 79 | COMMAND ${CMAKE_COMMAND} -E copy $ $ 80 | VERBATIM 81 | ) 82 | endforeach() 83 | endfunction() 84 | -------------------------------------------------------------------------------- /test/test.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Meta C++ Tool and Library 3 | * Copyright (C) 2022 Keith Hammond 4 | * 5 | * This Source Code Form is subject to the terms of the Mozilla Public 6 | * License, v. 2.0. If a copy of the MPL was not distributed with this 7 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef TEST_TEST_HPP 11 | #define TEST_TEST_HPP 1 12 | 13 | #include 14 | 15 | class TestPredefined; 16 | 17 | namespace test{ 18 | class TestClassNS{ 19 | public: 20 | void test_member(std::string_view a); 21 | 22 | float test_member2(float a, float b){ return a + b; } 23 | }; 24 | } 25 | 26 | using TestUsing = test::TestClassNS; 27 | 28 | using namespace test; 29 | 30 | typedef TestClassNS TestTypedef; 31 | 32 | enum class [[special_enum]] TestEnum{ 33 | _0, _1 = 420, _2, 34 | a, b = 69, c, 35 | count 36 | }; 37 | 38 | class TestClass{ 39 | public: 40 | int m_0; 41 | float m_1; 42 | 43 | std::string_view f_0(std::string &str) const noexcept; 44 | }; 45 | 46 | class TestBase{ 47 | public: 48 | virtual ~TestBase() = default; 49 | }; 50 | 51 | class TestDerived: public TestBase, private TestClass{ 52 | public: 53 | }; 54 | 55 | template 56 | struct TestTemplateClass{ 57 | public: 58 | using pointer = T*; 59 | 60 | TestTemplateClass() = default; 61 | 62 | template 63 | TestTemplateClass(U &&val) noexcept(noexcept(T(std::forward(val)))) 64 | : m_value(std::forward(val)){} 65 | 66 | TestTemplateClass &operator=(const TestTemplateClass&) = delete; 67 | 68 | template 69 | void set_value(U &&v){ 70 | m_value = std::forward(v); 71 | } 72 | 73 | template 74 | TestTemplateClass rebind(U &&v){ 75 | return TestTemplateClass{std::forward(v)}; 76 | } 77 | 78 | const T &value() const noexcept{ return m_value; } 79 | 80 | pointer ptr() noexcept{ return &m_value; } 81 | 82 | private: 83 | T m_value; 84 | }; 85 | 86 | template 87 | TestTemplateClass(T&&) -> TestTemplateClass>; 88 | 89 | inline bool operator>(TestTemplateClass a, TestTemplateClass b){ 90 | return a.value() > b.value(); 91 | } 92 | 93 | namespace detail{ 94 | template 95 | class TestDerivedTemplateHelper: public TestTemplateClass...{ 96 | 97 | }; 98 | } 99 | 100 | namespace test{ 101 | class TestDerivedTemplate: public TestTemplateClass{}; 102 | } 103 | 104 | class [[test::attrib]] TestClassAttrib{ 105 | public: 106 | TestClassAttrib(){} 107 | ~TestClassAttrib(){} 108 | }; 109 | 110 | class [[test::attrib, foo::bar(1, "2", '3', 4.0, 5.f)]] TestClass2Attribs{ 111 | public: 112 | TestClass2Attribs(){} 113 | ~TestClass2Attribs(){} 114 | }; 115 | 116 | class [[test::predefined]] TestPredefined{}; 117 | 118 | [[other::attrib(with, "args")]] 119 | inline void testFn(int a, TestClass b){} 120 | 121 | #endif // !TEST_TEST_HPP 122 | -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Meta C++ Tool and Library 3 | * Copyright (C) 2022 Keith Hammond 4 | * 5 | * This Source Code Form is subject to the terms of the Mozilla Public 6 | * License, v. 2.0. If a copy of the MPL was not distributed with this 7 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "fmt/format.h" 16 | 17 | #include "metacpp/meta.hpp" 18 | #include "metacpp/ast.hpp" 19 | #include "metacpp/refl.hpp" 20 | #include "metacpp/serial.hpp" 21 | 22 | #include "test.hpp" 23 | #include "test.meta.hpp" 24 | 25 | namespace fs = std::filesystem; 26 | 27 | template 28 | [[noreturn]] void exit_with_error(Str &&fmt_str, Args &&... args){ 29 | auto msg = fmt::vformat(std::forward(fmt_str), fmt::make_format_args(std::forward(args)...)); 30 | fmt::print(stderr, "[ERROR] {}\n", msg); 31 | std::exit(EXIT_FAILURE); 32 | } 33 | 34 | int main(int argc, char *argv[]){ 35 | fs::path exe_path = fs::absolute(argv[0]); 36 | 37 | if(argc != 3){ 38 | fmt::print("Usage: {} ", exe_path.filename().string()); 39 | std::exit(EXIT_FAILURE); 40 | } 41 | 42 | fs::path build_dir = argv[1]; 43 | fs::path header_path = argv[2]; 44 | 45 | if(!fs::exists(build_dir)){ 46 | exit_with_error("Build dir '{}' does not exist", build_dir.string()); 47 | } 48 | else if(!fs::is_directory(build_dir)){ 49 | exit_with_error("'{}' is not a directory", build_dir.string()); 50 | } 51 | 52 | if(!fs::exists(header_path)){ 53 | exit_with_error("Header '{}' does not exist", header_path.string()); 54 | } 55 | else if(!fs::is_regular_file(header_path)){ 56 | exit_with_error("'{}' is not a regular file", header_path.string()); 57 | } 58 | 59 | auto compile_info = ast::compile_info(build_dir); 60 | auto info = ast::parse(header_path, compile_info); 61 | 62 | auto test_type = refl::reflect(meta::type_name); 63 | 64 | if(!test_type){ 65 | exit_with_error("Failed to reflect '{}'", meta::type_name); 66 | } 67 | 68 | using test_info = meta::class_info; 69 | using test_attribs = meta::attributes; 70 | 71 | auto test_cls = dynamic_cast(test_type); 72 | 73 | TestClass test_val; 74 | test_val.m_0 = 69; 75 | test_val.m_1 = 420.f; 76 | 77 | assert(serial::to_json(test_val) == R"({"TestClass":[{"member":{"name":"m_0","type":"int","value":"69"}},{"member":{"name":"m_1","type":"float","value":"420"}}]})"); 78 | 79 | using namespace std::string_view_literals; 80 | 81 | assert(test_cls && "could not cast to refl::class_info"); 82 | 83 | assert(test_info::name == test_type->name()); 84 | assert(test_info::methods::size == test_cls->num_methods()); 85 | 86 | using attrib_results = meta::query_attribs; 87 | using method_results = meta::query_methods; 88 | 89 | static_assert(method_results::size > 0); 90 | static_assert(test_attribs::size == 2); 91 | static_assert(attrib_results::size == 1); 92 | 93 | using test_attrib = meta::get_t; 94 | 95 | static_assert(test_attrib::args::size == 5); 96 | 97 | // 1, "2", '3', 4.0, 5.f 98 | 99 | static_assert(meta::get_t::value == R"(1)"); 100 | static_assert(meta::get_t::value == R"("2")"); 101 | static_assert(meta::get_t::value == R"('3')"); 102 | static_assert(meta::get_t::value == R"(4.0)"); 103 | static_assert(meta::get_t::value == R"(5.f)"); 104 | 105 | static_assert(meta::get_value("_0") == TestEnum::_0); 106 | static_assert(meta::get_value("_1") == TestEnum::_1); 107 | static_assert(meta::get_value("_2") == TestEnum::_2); 108 | static_assert(meta::get_value("a") == TestEnum::a); 109 | static_assert(meta::get_value("b") == TestEnum::b); 110 | static_assert(meta::get_value("c") == TestEnum::c); 111 | 112 | return EXIT_SUCCESS; 113 | } 114 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 4 | 5 | cmake_minimum_required(VERSION 3.21 FATAL_ERROR) 6 | 7 | project(metacpp VERSION 0.1.0 LANGUAGES C CXX) 8 | 9 | set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) 10 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 11 | 12 | ################### 13 | # Project Options # 14 | ################### 15 | 16 | option(METACPP_BUILD_TESTS "Whether to build test targets" ${PROJECT_IS_TOP_LEVEL}) 17 | option(METACPP_BUILD_DOCS "Whether to build source documentation" ${PROJECT_IS_TOP_LEVEL}) 18 | set(METACPP_SMALL_SIZE 16 CACHE STRING "Size considered 'small'") 19 | 20 | ######################### 21 | # Project Configuration # 22 | ######################### 23 | 24 | set(METACPP_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) 25 | set(METACPP_VERSION_MINOR ${PROJECT_VERSION_MINOR}) 26 | set(METACPP_VERSION_PATCH ${PROJECT_VERSION_PATCH}) 27 | 28 | execute_process( 29 | COMMAND git rev-parse --short HEAD 30 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} 31 | OUTPUT_VARIABLE METACPP_REPO_HASH 32 | OUTPUT_STRIP_TRAILING_WHITESPACE 33 | ) 34 | 35 | execute_process( 36 | COMMAND git rev-parse --abbrev-ref HEAD 37 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} 38 | OUTPUT_VARIABLE METACPP_REPO_BRANCH 39 | OUTPUT_STRIP_TRAILING_WHITESPACE 40 | ) 41 | 42 | include(CheckIPOSupported) 43 | check_ipo_supported(RESULT METACPP_IPO_SUPPORTED OUTPUT METACPP_IPO_ERROR) 44 | 45 | set( 46 | METACPP_INCLUDE_DIR 47 | ${CMAKE_CURRENT_LIST_DIR}/include 48 | ) 49 | 50 | set( 51 | METACPP_RESOURCES 52 | README.md 53 | LICENSE 54 | ) 55 | 56 | set( 57 | METACPP_INCLUDE_DIRS 58 | ${CMAKE_CURRENT_LIST_DIR}/include 59 | ${CMAKE_CURRENT_BINARY_DIR}/include 60 | ) 61 | 62 | set( 63 | METACPP_INCLUDES 64 | ${METACPP_INCLUDE_DIR}/metacpp/meta.hpp 65 | ${METACPP_INCLUDE_DIR}/metacpp/refl.hpp 66 | ${METACPP_INCLUDE_DIR}/metacpp/plugin.hpp 67 | ${METACPP_INCLUDE_DIR}/metacpp/ast.hpp 68 | ${METACPP_INCLUDE_DIR}/metacpp/serial.hpp 69 | ${CMAKE_CURRENT_BINARY_DIR}/include/metacpp/config.hpp 70 | ) 71 | 72 | configure_file(include/metacpp/config.hpp.in ${CMAKE_CURRENT_BINARY_DIR}/include/metacpp/config.hpp) 73 | 74 | ################ 75 | # Dependencies # 76 | ################ 77 | 78 | include(FetchContent) 79 | 80 | find_package(Boost CONFIG COMPONENTS system dll) 81 | 82 | if(NOT Boost_system_FOUND) 83 | FetchContent_Declare( 84 | boostlib 85 | GIT_REPOSITORY https://github.com/boostorg/boost.git 86 | GIT_TAG a07c63c9e56f4d3b5bc904e25a7ad6f900728dbf # boost-1.78.0 87 | ) 88 | 89 | FetchContent_MakeAvailable(boostlib) 90 | elseif(NOT Boost_dll_FOUND) 91 | message(WARNING "Could not find Boost::dll, trying to continue anyway") 92 | endif() 93 | 94 | if(NOT TARGET fmt) 95 | find_package(fmt CONFIG) 96 | endif() 97 | 98 | if(NOT TARGET fmt AND NOT fmt_FOUND) 99 | FetchContent_Declare( 100 | fmtlib 101 | GIT_REPOSITORY https://github.com/fmtlib/fmt.git 102 | GIT_TAG b6f4ceaed0a0a24ccf575fab6c56dd50ccf6f1a9 # 8.1.1 103 | ) 104 | 105 | FetchContent_MakeAvailable(fmtlib) 106 | endif() 107 | 108 | ################### 109 | # Project Targets # 110 | ################### 111 | 112 | add_library(metacpp-headers INTERFACE ${METACPP_RESOURCES} ${METACPP_INCLUDES}) 113 | target_compile_features(metacpp-headers INTERFACE cxx_std_17) 114 | target_include_directories(metacpp-headers INTERFACE ${METACPP_INCLUDE_DIRS}) 115 | 116 | add_library(metacpp::headers ALIAS metacpp-headers) 117 | 118 | add_library(metacpp-attribs INTERFACE) 119 | 120 | if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") 121 | message(WARNING "MSVC not fully supported") 122 | else() 123 | target_compile_options(metacpp-attribs INTERFACE "-Wno-attributes") 124 | endif() 125 | 126 | add_library(metacpp::attribs ALIAS metacpp-attribs) 127 | add_library(metapp::attribs ALIAS metacpp-attribs) 128 | 129 | add_subdirectory(ast) 130 | add_subdirectory(plugin) 131 | add_subdirectory(refl) 132 | 133 | if(METACPP_BUILD_TESTS) 134 | add_subdirectory(test) 135 | endif() 136 | 137 | ######################### 138 | # Project Documentation # 139 | ######################### 140 | 141 | if(METACPP_BUILD_DOCS) 142 | find_package(Doxygen) 143 | 144 | if(DOXYGEN_FOUND) 145 | 146 | endif() 147 | endif() 148 | 149 | ######################## 150 | # Project Installation # 151 | ######################## 152 | 153 | include(CMakePackageConfigHelpers) 154 | include(GNUInstallDirs) 155 | 156 | set(METACPP_INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE STRING "Where to install header files") 157 | set(METACPP_INSTALL_LIBDIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING "Where to install libraries") 158 | set(METACPP_INSTALL_SYSCONFDIR ${CMAKE_INSTALL_SYSCONFDIR} CACHE STRING "Where to install configuration files") 159 | 160 | configure_package_config_file( 161 | ${CMAKE_CURRENT_LIST_DIR}/cmake/metacpp-config.cmake.in 162 | ${CMAKE_CURRENT_BINARY_DIR}/metacpp-config.cmake 163 | INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/metacpp/cmake 164 | PATH_VARS METACPP_INCLUDE_INSTALL_DIR METACPP_INSTALL_SYSCONFDIR 165 | ) 166 | 167 | write_basic_package_version_file( 168 | ${CMAKE_CURRENT_BINARY_DIR}/metacpp-config-version.cmake 169 | VERSION "${metacpp_VERSION_MAJOR}.${metacpp_VERSION_MINOR}.${metacpp_VERSION_PATCH}" 170 | COMPATIBILITY SameMajorVersion 171 | ) 172 | 173 | install(TARGETS reflpp metacpp-ast metacpp-refl metacpp-plugin) 174 | 175 | install( 176 | DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/include/metacpp" 177 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 178 | FILES_MATCHING PATTERN "*.hpp" 179 | ) 180 | 181 | install( 182 | FILES 183 | ${CMAKE_CURRENT_BINARY_DIR}/metacpp-config.cmake 184 | ${CMAKE_CURRENT_BINARY_DIR}/metacpp-config-version.cmake 185 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/metacpp/cmake 186 | ) 187 | -------------------------------------------------------------------------------- /test/example.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Meta C++ Tool and Library 3 | * Copyright (C) 2022 Keith Hammond 4 | * 5 | * This Source Code Form is subject to the terms of the Mozilla Public 6 | * License, v. 2.0. If a copy of the MPL was not distributed with this 7 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #include 11 | 12 | #include "example.meta.h" 13 | 14 | int main(int argc, char *argv[]){ 15 | // -------------- 16 | // C++17 Features 17 | // -------------- 18 | 19 | // iterate over all public class methods 20 | meta::for_all>([](auto info_type){ 21 | 22 | // retrieve the method info (not needed with c++20) 23 | using method = meta::get_t; 24 | 25 | // get method names 26 | std::cout << "Method " << method::name << "\n"; 27 | 28 | // as well as information about the method 29 | std::cout << "\t" "pointer type: " << meta::type_name << "\n"; 30 | 31 | // iterate over every parameter of a method 32 | meta::for_all_i([](auto param, std::size_t idx){ 33 | 34 | // 'param' is a meta::type 35 | using info = meta::get_t; 36 | 37 | // get parameter types 38 | using param_type = typename info::type; 39 | 40 | // and names 41 | std::cout << "\t" "parameter '" << info::name << "': " << meta::type_name << "\n"; 42 | 43 | }); 44 | 45 | // and don't forget the result type 46 | std::cout << "\t" "result type: " << meta::type_name << "\n"; 47 | }); 48 | 49 | // iterate over all public class members 50 | meta::for_all>([](auto info_type){ 51 | using member = meta::get_t; 52 | 53 | std::cout << "Member " << member::name << "\n"; 54 | 55 | std::cout << "\t" "pointer type: " << meta::type_name << "\n"; 56 | 57 | meta::for_all([](auto attrib_info_type){ 58 | using attrib = meta::get_t; 59 | 60 | if constexpr(attrib::scope.empty()){ 61 | std::cout << "\t" "attribute name: " << attrib::name << "\n"; 62 | } 63 | else{ 64 | std::cout << "\t" "attribute name: " << attrib::scope << "::" << attrib::name << "\n"; 65 | } 66 | 67 | if constexpr(attrib::args::size != 0){ 68 | meta::for_all([](auto arg_info_type){ 69 | using arg = meta::get_t; 70 | std::cout << "\t" "attribute arg: " << arg::value << "\n"; 71 | }); 72 | } 73 | }); 74 | 75 | }); 76 | 77 | // alias the info for the example class 78 | using example_info = meta::class_info; 79 | 80 | // do some checks on the attributes 81 | static_assert(example_info::attributes::size == 1); 82 | 83 | // handy shortcut for getting the attributes of a type 84 | using my_attribs = meta::attributes; 85 | 86 | // get the only attribute; by default 0 is passed as the second argument to get_t 87 | using my_attrib = meta::get_t; 88 | 89 | // this can make the signal-to-noise ratio better 90 | using meta::get_t; 91 | using meta::for_all; 92 | using meta::for_all_i; 93 | 94 | // we can even inspect the arguments! 95 | using my_attrib_args = typename my_attrib::args; 96 | static_assert(my_attrib_args::size == 3); 97 | 98 | // do some checking on the attribute arguments 99 | static_assert(get_t::value == R"(1)"); 100 | static_assert(get_t::value == R"("2")"); 101 | static_assert(get_t::value == R"(3.0)"); 102 | 103 | // iterate over all class attributes 104 | for_all>([](auto attrib){ 105 | using info = get_t; 106 | 107 | // supports [[scoped::attributes]] 108 | if constexpr(!info::scope.empty()){ 109 | std::cout << info::scope << "::"; 110 | } 111 | 112 | // get attribute names 113 | std::cout << info::name; 114 | 115 | // also supports [[attributes(with, "args")]] 116 | if constexpr(!info::args::empty){ 117 | std::cout << "("; 118 | 119 | // iterate over all attribute arguments 120 | for_all_i([]{ // template lambdas are a c++20 feature 121 | if constexpr(Idx != 0){ 122 | std::cout << ", "; 123 | } 124 | 125 | std::cout << Arg::value; 126 | }); 127 | 128 | std::cout << ")"; 129 | } 130 | }); 131 | 132 | // we can also reflect enums 133 | using enum_info = meta::enum_info; 134 | 135 | // get the name of an enum and if it is scoped 136 | std::cout << (enum_info::is_scoped ? "Scoped " : "") << "Enum " << enum_info::name << "\n"; 137 | 138 | // iterate over all enum values 139 | for_all([](auto value){ 140 | using info = get_t; 141 | 142 | // get the value name and unsigned integer representation 143 | std::cout << "\t" "Enum value '" << info::name << "' == " << info::value << "\n"; 144 | 145 | }); 146 | 147 | // Get enum values from strings at compile time 148 | static_assert(meta::get_value("case_0") == static_cast(69)); 149 | static_assert(meta::get_value("case_1") == static_cast(420)); 150 | static_assert(meta::get_value("case_2") == static_cast(1337)); 151 | 152 | // -------------- 153 | // C++20 Features 154 | // -------------- 155 | 156 | // do compile-time queries on class info 157 | static_assert(example_info::query_methods<"method1">::size == 2); 158 | 159 | // specify a signature 160 | static_assert(example_info::query_methods<"method1", void(std::string_view)>::size == 1); 161 | 162 | // or just part of one 163 | static_assert(example_info::query_methods<"method2", meta::ignore()>::size == 1); 164 | 165 | // also supports attribute queries 166 | static_assert(example_info::query_attributes<"my", "attrib">::size == 1); 167 | } 168 | -------------------------------------------------------------------------------- /refl/refl.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Meta C++ Tool and Library 3 | * Copyright (C) 2022 Keith Hammond 4 | * 5 | * This Source Code Form is subject to the terms of the Mozilla Public 6 | * License, v. 2.0. If a copy of the MPL was not distributed with this 7 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #include 11 | 12 | #include "fmt/format.h" 13 | 14 | #include "metacpp/refl.hpp" 15 | 16 | namespace { 17 | struct void_info_helper: refl::detail::type_info_helper{ 18 | std::string_view name() const noexcept override{ return "void"; } 19 | std::size_t size() const noexcept override{ return 0; } 20 | std::size_t alignment() const noexcept override{ return 0; } 21 | void destroy(void*) const noexcept override{} 22 | void *construct(void *p, refl::args_pack_base *args) const override{ return nullptr; } 23 | std::type_index type_index() const noexcept override{ return typeid(void); } 24 | }; 25 | 26 | static refl::detail::int_info_helper_impl int8_refl; 27 | static refl::detail::int_info_helper_impl int16_refl; 28 | static refl::detail::int_info_helper_impl int32_refl; 29 | static refl::detail::int_info_helper_impl int64_refl; 30 | 31 | static refl::detail::int_info_helper_impl uint8_refl; 32 | static refl::detail::int_info_helper_impl uint16_refl; 33 | static refl::detail::int_info_helper_impl uint32_refl; 34 | static refl::detail::int_info_helper_impl uint64_refl; 35 | 36 | static refl::detail::float_info_helper_impl float_refl; 37 | static refl::detail::float_info_helper_impl double_refl; 38 | 39 | class type_loader{ 40 | public: 41 | type_loader(){} 42 | 43 | ~type_loader(){} 44 | 45 | refl::type_info load(std::string_view name){ 46 | if(!m_types.empty()){ 47 | auto registered_res = m_types.find(name); 48 | if(registered_res != m_types.end()){ 49 | return registered_res->second; 50 | } 51 | } 52 | 53 | //fmt::print(stderr, "Failed to import reflected type '{}'\n", name); 54 | 55 | return nullptr; 56 | } 57 | 58 | bool register_type(refl::type_info info, bool overwrite){ 59 | if(!m_types.empty() && !overwrite){ 60 | auto res = m_types.find(info->name()); 61 | if(res != m_types.end()){ 62 | return false; 63 | } 64 | 65 | auto emplace_res = m_types.try_emplace(info->name(), info); 66 | if(!emplace_res.second){ 67 | return false; 68 | } 69 | } 70 | else{ 71 | m_types[info->name()] = info; 72 | } 73 | 74 | return true; 75 | } 76 | 77 | std::vector all(){ 78 | std::vector ret; 79 | ret.reserve(m_types.size() + 32); 80 | 81 | ret.emplace_back(&int8_refl); 82 | ret.emplace_back(&int16_refl); 83 | ret.emplace_back(&int32_refl); 84 | ret.emplace_back(&int64_refl); 85 | ret.emplace_back(&uint8_refl); 86 | ret.emplace_back(&uint16_refl); 87 | ret.emplace_back(&uint32_refl); 88 | ret.emplace_back(&uint64_refl); 89 | ret.emplace_back(&float_refl); 90 | ret.emplace_back(&double_refl); 91 | ret.emplace_back(refl::detail::void_info()); 92 | 93 | for(auto &&type_p : m_types){ 94 | ret.emplace_back(type_p.second); 95 | } 96 | 97 | return ret; 98 | } 99 | 100 | std::vector all_classes(){ 101 | std::vector ret; 102 | ret.reserve(m_types.size()); 103 | 104 | for(auto &&type_p : m_types){ 105 | auto cls = dynamic_cast(type_p.second); 106 | if(cls) ret.emplace_back(cls); 107 | } 108 | 109 | return ret; 110 | } 111 | 112 | private: 113 | std::unordered_map m_types; 114 | }; 115 | 116 | type_loader REFLCPP_EXPORT_SYMBOL loader; 117 | } 118 | 119 | refl::type_info refl::detail::void_info() noexcept{ 120 | static void_info_helper ret; 121 | return &ret; 122 | } 123 | 124 | refl::int_info refl::detail::int_info(std::size_t bits, bool is_signed) noexcept{ 125 | if(is_signed){ 126 | switch(bits){ 127 | case 8: return &uint8_refl; 128 | case 16: return &uint16_refl; 129 | case 32: return &uint32_refl; 130 | case 64: return &uint64_refl; 131 | default: return nullptr; 132 | } 133 | } 134 | else{ 135 | switch(bits){ 136 | case 8: return &int8_refl; 137 | case 16: return &int16_refl; 138 | case 32: return &int32_refl; 139 | case 64: return &int64_refl; 140 | default: return nullptr; 141 | } 142 | } 143 | } 144 | 145 | refl::num_info refl::detail::float_info(std::size_t bits) noexcept{ 146 | switch(bits){ 147 | case 32: return &float_refl; 148 | case 64: return &double_refl; 149 | default: return nullptr; 150 | } 151 | } 152 | 153 | bool refl::detail::register_type(refl::type_info info, bool overwrite){ 154 | return loader.register_type(info, overwrite); 155 | } 156 | 157 | refl::type_info refl::reflect(std::string_view name){ 158 | return loader.load(name); 159 | } 160 | 161 | std::vector refl::reflect_all(){ 162 | return loader.all(); 163 | } 164 | 165 | std::vector refl::reflect_all_classes(){ 166 | return loader.all_classes(); 167 | } 168 | 169 | refl::class_info refl::reflect_class(std::string_view name){ 170 | auto ret = reflect(name); 171 | return dynamic_cast(ret); 172 | } 173 | 174 | refl::enum_info refl::reflect_enum(std::string_view name){ 175 | auto ret = reflect(name); 176 | return dynamic_cast(ret); 177 | } 178 | 179 | std::vector refl::attribute(refl::type_info t, std::string_view name, std::vector placeholder_){ 180 | const std::size_t num_attribs = t->num_attributes(); 181 | 182 | std::string attrib_name; 183 | 184 | for(std::size_t attrib_i = 0; attrib_i < num_attribs; attrib_i++){ 185 | const auto attrib = t->attribute(attrib_i); 186 | const auto scope = attrib->scope(); 187 | 188 | if(scope.empty()){ 189 | attrib_name = attrib->name(); 190 | } 191 | else{ 192 | attrib_name = fmt::format("{}::{}", scope, attrib->name()); 193 | } 194 | 195 | if(attrib_name == name){ 196 | const std::size_t num_args = attrib->num_args(); 197 | 198 | std::vector ret; 199 | ret.reserve(num_args); 200 | 201 | for(std::size_t arg_i = 0; arg_i < num_args; arg_i++){ 202 | ret.emplace_back(attrib->arg(arg_i)); 203 | } 204 | 205 | return ret; 206 | } 207 | } 208 | 209 | return placeholder_; 210 | } 211 | 212 | bool refl::has_base(refl::class_info type, refl::class_info base) noexcept{ 213 | if(!type || !base) return false; 214 | 215 | for(std::size_t i = 0; i < type->num_bases(); i++){ 216 | auto type_base = type->base(i); 217 | if(type_base == base || refl::has_base(type_base, base)){ 218 | return true; 219 | } 220 | } 221 | 222 | return false; 223 | } 224 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Meta C++ 2 | 3 | This project contains a library for C++ AST parsing, metaprogramming and reflection. 4 | 5 | Also included is a tool for generating the necessary meta and reflection information for use by each of the respective libraries. 6 | 7 | ## Dependencies 8 | 9 | System dependencies: 10 | - [CMake](https://cmake.org/) 3.21+ 11 | - A C++17 compiler 12 | - [Libclang](https://clang.llvm.org/) 13 | 14 | External dependencies (will be found or downloaded as required): 15 | - [Boost](https://www.boost.org/) system 16 | - [{fmt}](https://fmt.dev/) 17 | 18 | ## Including in a project 19 | 20 | Although standalone compilation is supported and is important for bug-testing and development, the entire project is intended to be included as a subproject. 21 | 22 | ### Fetching the library 23 | 24 | #### Using `FetchContent` (recommended) 25 | 26 | From within your projects main `CMakeLists.txt` add the following before any targets that depend on the library: 27 | 28 | ```cmake 29 | FetchContent_Declare( 30 | metapp 31 | GIT_REPOSITORY https://github.com/RamblingMadMan/metacpp.git 32 | ) 33 | 34 | FetchContent_MakeAvailable(metapp) 35 | ``` 36 | 37 | #### As a git submodule 38 | 39 | From the source directory of a project initialized with git run the following command: 40 | 41 | ```bash 42 | git submodule add --depth 1 https://github.com/RamblingMadMan/metacpp.git deps/metacpp 43 | ``` 44 | 45 | Replace `deps/metacpp` with your own location if required. 46 | 47 | Then from within your projects main `CMakeLists.txt` add the following before any targets that depend on the library: 48 | 49 | ```cmake 50 | add_subdirectory(deps/metacpp) 51 | ``` 52 | 53 | ### Generating information 54 | 55 | For any targets that will use the `meta` or `refl` libraries, add the following to the correct `CMakeLists.txt`: 56 | 57 | ```cmake 58 | target_reflect() 59 | ``` 60 | 61 | ## Usage 62 | 63 | ### `CMakeLists.txt`: 64 | 65 | ```cmake 66 | add_executable(example example.h example.cpp) 67 | 68 | target_reflect(example) 69 | ``` 70 | 71 | ### `example.h`: 72 | 73 | ```c++ 74 | #pragma once 75 | 76 | #include 77 | #include 78 | 79 | class [[my::attrib(1, "2", 3.0)]] example{ 80 | public: 81 | void method1(std::string_view s) const noexcept; 82 | void method1(const std::string &str) noexcept; 83 | 84 | std::tuple method2(); 85 | }; 86 | 87 | enum class example_enum{ 88 | case_0 = 69, 89 | case_1 = 420, 90 | case_2 = 1337 91 | }; 92 | 93 | ``` 94 | 95 | ### `example.cpp`: 96 | 97 | ```c++ 98 | #include 99 | 100 | #include "example.meta.h" 101 | 102 | int main(int argc, char *argv[]){ 103 | // -------------- 104 | // C++17 Features 105 | // -------------- 106 | 107 | // iterate over all public class methods 108 | meta::for_all>([]{ 109 | 110 | // get method names 111 | std::cout << "Method " << Method::name << "\n"; 112 | 113 | // as well as information about the method 114 | std::cout << "\t" "pointer type: " << meta::type_name << "\n"; 115 | 116 | // iterate over every parameter of a method 117 | meta::for_all_i([](auto param, std::size_t idx){ 118 | 119 | // 'param' is a meta::type 120 | using info = meta::get_t; 121 | 122 | // get parameter types 123 | using param_type = typename info::type; 124 | 125 | // and names 126 | std::cout << "\t" "parameter '" << info::name << "': " << meta::type_name << "\n"; 127 | 128 | }); 129 | 130 | // and don't forget the result type 131 | std::cout << "\t" "result type: " << meta::type_name << "\n"; 132 | }); 133 | 134 | // alias the info for the example class 135 | using example_info = meta::class_info; 136 | 137 | // do some checks on the attributes 138 | static_assert(example_info::attributes::size == 1); 139 | 140 | // handy shortcut for getting the attributes of a type 141 | using my_attribs = meta::attributes; 142 | 143 | // get the only attribute; by default 0 is passed as the second argument to get_t 144 | using my_attrib = meta::get_t; 145 | 146 | // this can make the signal-to-noise ratio better 147 | using meta::get_t; 148 | using meta::for_all; 149 | using meta::for_all_i; 150 | 151 | // we can even inspect the arguments! 152 | using my_attrib_args = typename my_attrib::args; 153 | static_assert(my_attrib_args::size == 3); 154 | 155 | // do some checking on the attribute arguments 156 | static_assert(get_t::value == R"(1)"); 157 | static_assert(get_t::value == R"("2")"); 158 | static_assert(get_t::value == R"(3.0)"); 159 | 160 | // iterate over all class attributes 161 | for_all>([](auto attrib){ 162 | using info = get_t; 163 | 164 | // supports [[scoped::attributes]] 165 | if constexpr(!info::scope.empty()){ 166 | std::cout << info::scope << "::"; 167 | } 168 | 169 | // get attribute names 170 | std::cout << info::name; 171 | 172 | // also supports [[attributes(with, "args")]] 173 | if constexpr(!info::args::empty){ 174 | std::cout << "("; 175 | 176 | // iterate over all attribute arguments 177 | for_all_i([]{ 178 | if constexpr(Idx != 0){ 179 | std::cout << ", "; 180 | } 181 | 182 | std::cout << Arg::value; 183 | }); 184 | 185 | std::cout << ")"; 186 | } 187 | }); 188 | 189 | // we can also reflect enums 190 | using enum_info = meta::enum_info; 191 | 192 | // get the name of an enum and if it is scoped 193 | std::cout << (enum_info::is_scoped ? "Scoped " : "") << "Enum " << enum_info::name << "\n"; 194 | 195 | // iterate over all enum values 196 | for_all([](auto value){ 197 | using info = get_t; 198 | 199 | // get the value name and unsigned integer representation 200 | std::cout << "\t" "Enum value '" << info::name << "' == " << info::value << "\n"; 201 | 202 | }); 203 | 204 | // Get enum values from strings at compile time 205 | static_assert(meta::get_value("case_0") == static_cast(69)); 206 | static_assert(meta::get_value("case_1") == static_cast(420)); 207 | static_assert(meta::get_value("case_2") == static_cast(1337)); 208 | 209 | // -------------- 210 | // C++20 Features 211 | // -------------- 212 | 213 | // do compile-time queries on class info 214 | static_assert(example_info::query_methods<"method1">::size == 2); 215 | 216 | // specify a signature 217 | static_assert(example_info::query_methods<"method1", void(std::string_view)>::size == 1); 218 | 219 | // or just part of one 220 | static_assert(example_info::query_methods<"method2", meta::ignore()>::size == 1); 221 | 222 | // also supports attribute queries 223 | static_assert(example_info::query_attributes<"my", "attrib">::size == 1); 224 | } 225 | 226 | ``` 227 | 228 | ## Bugs 229 | 230 | Still in early development phase, so lots of bugs to be found. 231 | 232 | If you do find any bugs; please, create an issue in this repo so I can prompty get them fixed. 233 | -------------------------------------------------------------------------------- /refl/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 4 | 5 | find_package(Threads REQUIRED) 6 | 7 | add_library(metacpp-refl SHARED make_meta.hpp refl.cpp) 8 | add_library(metacpp::refl ALIAS metacpp-refl) 9 | add_library(metapp::refl ALIAS metacpp-refl) 10 | 11 | target_compile_features(metacpp-refl PUBLIC cxx_std_17) 12 | 13 | target_link_libraries(metacpp-refl PUBLIC metacpp::headers fmt::fmt-header-only) 14 | target_link_libraries(metacpp-refl PRIVATE Boost::system) 15 | 16 | set_target_properties( 17 | metacpp-refl PROPERTIES POSITION_INDEPENDENT_CODE ON 18 | ) 19 | 20 | if(CMAKE_CROSSCOMPILING AND WIN32) 21 | find_program(REFLPP_EXECUTABLE_RES reflpp REQUIRED) 22 | set(REFLPP_EXECUTABLE ${REFLPP_EXECUTABLE_RES} CACHE STRING "Executable for reflecting targets") 23 | else() 24 | set(REFLPP_EXECUTABLE reflpp CACHE STRING "Executable for reflecting targets") 25 | endif() 26 | 27 | add_executable(reflpp tool.cpp) 28 | 29 | add_executable(metacpp::refl-tool ALIAS reflpp) 30 | add_executable(metacpp::refl-tool ALIAS reflpp) 31 | add_executable(metapp::refl-tool ALIAS reflpp) 32 | 33 | target_compile_features(reflpp PRIVATE cxx_std_17) 34 | 35 | target_link_libraries(reflpp Threads::Threads Boost::system fmt::fmt-header-only metacpp::ast) 36 | 37 | if(METACPP_IPO_SUPPORTED) 38 | set_target_properties( 39 | reflpp PROPERTIES INTERPROCEDURAL_OPTIMIZATION ON 40 | ) 41 | endif() 42 | 43 | set(REFLPP_DEFAULT_CFLAGS ) 44 | 45 | foreach(STD_DIR IN LISTS CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES) 46 | list(APPEND REFLPP_DEFAULT_CFLAGS "-I${STD_DIR}") 47 | endforeach() 48 | 49 | set(REFLPP_DEFAULT_CFLAGS "${REFLPP_DEFAULT_CFLAGS}" PARENT_SCOPE) 50 | 51 | function(target_reflect tgt) 52 | message(STATUS "Generating reflection information for ${tgt}") 53 | 54 | get_target_property(TGT_TYPE ${tgt} TYPE) 55 | 56 | if(${TGT_TYPE} STREQUAL "INTERFACE_LIBRARY") 57 | target_link_libraries(${tgt} INTERFACE metacpp::headers metacpp::refl) 58 | else() 59 | target_link_libraries(${tgt} PRIVATE metacpp::headers metacpp::refl) 60 | endif() 61 | 62 | if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") 63 | message(WARNING "MSVC not fully supported") 64 | else() 65 | if(${TGT_TYPE} STREQUAL "INTERFACE_LIBRARY") 66 | target_compile_options(${tgt} INTERFACE "-Wno-attributes") 67 | else() 68 | target_compile_options(${tgt} PUBLIC "-Wno-attributes") 69 | endif() 70 | endif() 71 | 72 | get_target_property(TGT_BINARY_DIR ${tgt} BINARY_DIR) 73 | get_target_property(TGT_INCLUDE_DIRS ${tgt} INCLUDE_DIRECTORIES) 74 | get_target_property(TGT_INTERFACE_INCLUDE_DIRS ${tgt} INTERFACE_INCLUDE_DIRECTORIES) 75 | get_target_property(TGT_SOURCE_DIR ${tgt} SOURCE_DIR) 76 | 77 | list(APPEND TGT_INCLUDE_DIRS "${TGT_INTERFACE_INCLUDE_DIRS}") 78 | 79 | if(${TGT_TYPE} STREQUAL "INTERFACE_LIBRARY") 80 | get_target_property(TGT_SOURCES ${tgt} INTERFACE_SOURCES) 81 | else() 82 | get_target_property(TGT_SOURCES ${tgt} SOURCES) 83 | endif() 84 | 85 | set_target_properties( 86 | ${tgt} PROPERTIES 87 | EXPORT_COMPILE_COMMANDS ON 88 | ENABLE_EXPORTS ON 89 | ) 90 | 91 | if(${TGT_TYPE} STREQUAL "INTERFACE_LIBRARY") 92 | target_compile_features(${tgt} INTERFACE cxx_std_17) 93 | else() 94 | target_compile_features(${tgt} PRIVATE cxx_std_17) 95 | endif() 96 | 97 | get_target_property(TGT_CXX_STANDARD ${tgt} CXX_STANDARD) 98 | 99 | if(TGT_CXX_STANDARD LESS 17) 100 | message("-- Changing CXX_STANDARD for ${tgt} from ${TGT_CXX_STANDARD} to 17") 101 | set_target_properties( 102 | ${tgt} PROPERTIES 103 | CXX_STANDARD 17 104 | CXX_STANDARD_REQUIRED ON 105 | ) 106 | endif() 107 | 108 | if(${TGT_TYPE} STREQUAL "EXECUTABLE") 109 | set_target_properties( 110 | ${tgt} PROPERTIES 111 | POSITION_INDEPENDENT_CODE OFF 112 | ENABLE_EXPORTS ON 113 | ) 114 | endif() 115 | 116 | set(INPUT_HEADERS "") 117 | 118 | string(MD5 TGT_HASH "${tgt}") 119 | 120 | set(OUTPUT_DIR "${TGT_BINARY_DIR}/reflect/${TGT_HASH}") 121 | set(OUTPUT_HEADERS "") 122 | set(OUTPUT_SOURCES "") 123 | 124 | file(MAKE_DIRECTORY ${OUTPUT_DIR}) 125 | 126 | if(${TGT_TYPE} STREQUAL "INTERFACE_LIBRARY") 127 | target_include_directories(${tgt} INTERFACE ${OUTPUT_DIR}) 128 | else() 129 | target_include_directories(${tgt} PUBLIC ${OUTPUT_DIR}) 130 | endif() 131 | 132 | foreach(SRC IN LISTS TGT_SOURCES) 133 | cmake_path(GET SRC EXTENSION TGT_SRC_EXT) 134 | if(TGT_SRC_EXT MATCHES "(\\.hpp)|(\\.h)") 135 | cmake_path(ABSOLUTE_PATH SRC OUTPUT_VARIABLE SRC_ABSOLUTE) 136 | 137 | message(DEBUG "header path: ${SRC_ABSOLUTE}") 138 | 139 | set(TGT_OUTPUT_DIR "${OUTPUT_DIR}") 140 | 141 | foreach(dir IN LISTS TGT_INCLUDE_DIRS) 142 | cmake_path(ABSOLUTE_PATH dir OUTPUT_VARIABLE DIR_ABSOLUTE) 143 | 144 | string(LENGTH "${DIR_ABSOLUTE}/" DIR_LENGTH) 145 | string(SUBSTRING "${SRC_ABSOLUTE}" 0 ${DIR_LENGTH} PATH_INITIAL) 146 | 147 | message(DEBUG "include dir: ${DIR_ABSOLUTE}") 148 | 149 | if(PATH_INITIAL STREQUAL "${DIR_ABSOLUTE}/") 150 | cmake_path(GET SRC_ABSOLUTE PARENT_PATH PATH_DIR) 151 | string(SUBSTRING "${PATH_DIR}" ${DIR_LENGTH} -1 PATH_RELATIVE) 152 | set(TGT_OUTPUT_DIR "${OUTPUT_DIR}/${PATH_RELATIVE}") 153 | 154 | message(DEBUG "absolute path: ${SRC_ABSOLUTE}") 155 | message(DEBUG "path initial: ${PATH_INITIAL}") 156 | message(DEBUG "path parent: ${PATH_DIR}") 157 | message(DEBUG "path relative: ${PATH_RELATIVE}") 158 | 159 | break() 160 | endif() 161 | endforeach() 162 | 163 | list(APPEND INPUT_HEADERS "${SRC}") 164 | 165 | cmake_path(GET SRC EXTENSION TGT_EXTENSION) 166 | cmake_path(GET SRC FILENAME TGT_HEADER) 167 | cmake_path(GET SRC FILENAME TGT_HEADER_OUT) 168 | cmake_path(REPLACE_EXTENSION TGT_HEADER_OUT ".meta${TGT_EXTENSION}" OUTPUT_VARIABLE TGT_HEADER_OUT) 169 | 170 | set(TGT_HEADER_OUTPUT "${TGT_OUTPUT_DIR}/${TGT_HEADER_OUT}") 171 | set(TGT_SOURCE_OUTPUT "${TGT_OUTPUT_DIR}/${TGT_HEADER}.refl.cpp") 172 | 173 | message(DEBUG "Meta information for '${SRC}' output to ${TGT_HEADER_OUTPUT}") 174 | 175 | list(APPEND OUTPUT_HEADERS "${TGT_HEADER_OUTPUT}") 176 | list(APPEND OUTPUT_SOURCES "${TGT_SOURCE_OUTPUT}") 177 | 178 | if(${TGT_TYPE} STREQUAL "INTERFACE_LIBRARY") 179 | target_sources(${tgt} INTERFACE "${TGT_HEADER_OUTPUT}") 180 | target_sources(${tgt} INTERFACE "${TGT_SOURCE_OUTPUT}") 181 | else() 182 | target_sources(${tgt} PUBLIC "${TGT_HEADER_OUTPUT}") 183 | target_sources(${tgt} PRIVATE "${TGT_SOURCE_OUTPUT}") 184 | endif() 185 | endif() 186 | endforeach() 187 | 188 | if(NOT OUTPUT_HEADERS STREQUAL "") 189 | if(CMAKE_CROSSCOMPILING) 190 | set(REFLPP_FLAGS "-d") 191 | else() 192 | set(REFLPP_FLAGS "") 193 | endif() 194 | 195 | add_custom_command( 196 | OUTPUT ${OUTPUT_SOURCES} ${OUTPUT_HEADERS} 197 | DEPENDS reflpp ${INPUT_HEADERS} 198 | COMMAND ${REFLPP_EXECUTABLE} ${REFLPP_FLAGS} -o "${OUTPUT_DIR}" "${PROJECT_BINARY_DIR}" ${INPUT_HEADERS} -- ${REFLPP_DEFAULT_CFLAGS} -std=gnu++20 199 | WORKING_DIRECTORY ${TGT_SOURCE_DIR} 200 | VERBATIM 201 | ) 202 | 203 | #target_sources(${tgt} PRIVATE ${OUTPUT_SOURCES}) 204 | endif() 205 | 206 | if(${TGT_TYPE} STREQUAL "INTERFACE_LIBRARY") 207 | target_include_directories(${tgt} INTERFACE ${OUTPUT_DIR}) 208 | else() 209 | target_include_directories(${tgt} PUBLIC ${OUTPUT_DIR}) 210 | endif() 211 | endfunction() 212 | -------------------------------------------------------------------------------- /plugin/plugin.cpp: -------------------------------------------------------------------------------- 1 | #include "metacpp/plugin.hpp" 2 | 3 | #include "fmt/format.h" 4 | 5 | #include "boost/core/demangle.hpp" 6 | 7 | #define BOOST_DLL_USE_STD_FS 1 8 | #include "boost/dll/library_info.hpp" 9 | #include "boost/dll/shared_library.hpp" 10 | #include "boost/dll/runtime_symbol_info.hpp" 11 | 12 | #ifdef __linux__ 13 | #include 14 | #elif defined(_WIN32) 15 | #include 16 | #else 17 | #error "Unsupported operating system" 18 | #endif 19 | 20 | namespace pluginpp::detail{ 21 | #ifdef __linux__ 22 | using lib_handle = void*; 23 | #else 24 | using lib_handle = HMODULE; 25 | #endif 26 | 27 | static lib_handle load_library(const char *path){ 28 | #ifdef __linux__ 29 | return dlopen(path, RTLD_LAZY); 30 | #else 31 | return path ? LoadLibraryA(path) : GetModuleHandleA(nullptr); 32 | #endif 33 | } 34 | 35 | static bool close_library(lib_handle handle){ 36 | #ifdef __linux__ 37 | return dlclose(handle) == 0; 38 | #else 39 | return (handle == GetModuleHandleA(nullptr)) ? true : FreeLibrary(handle); 40 | #endif 41 | } 42 | 43 | static void *get_symbol(lib_handle lib, const char *name){ 44 | #ifdef __linux__ 45 | return dlsym(lib, name); 46 | #else 47 | return reinterpret_cast(GetProcAddress(lib, name)); 48 | #endif 49 | } 50 | 51 | static const char *get_error(){ 52 | #ifdef __linux__ 53 | return dlerror(); 54 | #else 55 | thread_local char buf[256]; 56 | FormatMessage( 57 | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 58 | nullptr, GetLastError(), 59 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 60 | buf, sizeof(buf), 61 | nullptr 62 | ); 63 | 64 | return buf; 65 | #endif 66 | } 67 | } 68 | 69 | namespace fs = std::filesystem; 70 | namespace dll = boost::dll; 71 | 72 | using namespace pluginpp; 73 | 74 | namespace { 75 | static std::function print_fn = [](const std::string &msg){ 76 | fmt::print(stderr, "[ERROR] {}\n", msg); 77 | }; 78 | 79 | template 80 | void print_error(Str &&fmt_str, Args &&... args){ 81 | auto msg = fmt::vformat(std::forward(fmt_str), fmt::make_format_args(std::forward(args)...)); 82 | print_fn(msg); 83 | } 84 | 85 | class self_t{}; 86 | 87 | class dynamic_library: public library{ 88 | public: 89 | explicit dynamic_library(self_t) 90 | : m_handle(detail::load_library(nullptr)) 91 | { 92 | if(!m_handle){ 93 | auto msg = fmt::format("Error in load_library: {}", detail::get_error()); 94 | throw std::runtime_error(msg); 95 | } 96 | 97 | auto info = dll::library_info(dll::program_location()); 98 | 99 | m_symbols = info.symbols(); 100 | 101 | import_entities(); 102 | } 103 | 104 | explicit dynamic_library(const fs::path &path){ 105 | auto path_utf8 = path.u8string(); 106 | 107 | m_handle = detail::load_library(path.u8string().c_str()); 108 | if(!m_handle){ 109 | auto msg = fmt::format("Error in load_library: {}", detail::get_error()); 110 | throw std::runtime_error(msg); 111 | } 112 | 113 | auto info = dll::library_info(path); 114 | 115 | m_symbols = info.symbols(); 116 | 117 | import_entities(); 118 | } 119 | 120 | dynamic_library(const dynamic_library&) = delete; 121 | 122 | dynamic_library(dynamic_library &&other) noexcept 123 | : m_handle(std::exchange(other.m_handle, nullptr)) 124 | , m_symbols(std::move(other.m_symbols)) 125 | , m_types(std::move(other.m_types)) 126 | {} 127 | 128 | ~dynamic_library(){ 129 | reset(); 130 | } 131 | 132 | dynamic_library &operator=(const dynamic_library&) = delete; 133 | 134 | dynamic_library &operator=(dynamic_library &&other) noexcept{ 135 | if(this != &other){ 136 | m_symbols = std::move(other.m_symbols); 137 | m_types = std::move(other.m_types); 138 | reset(std::exchange(other.m_handle, nullptr)); 139 | } 140 | 141 | return *this; 142 | } 143 | 144 | bool is_valid() const noexcept{ return !!m_handle; } 145 | 146 | std::string demangle(const std::string &symbol_name) const noexcept override{ 147 | return boost::core::demangle(symbol_name.c_str()); 148 | } 149 | 150 | const std::vector &symbols() const noexcept override{ 151 | return m_symbols; 152 | } 153 | 154 | const std::vector &exported_types() const noexcept override{ 155 | return m_types; 156 | } 157 | 158 | const std::vector &exported_functions() const noexcept override{ 159 | return m_fns; 160 | } 161 | 162 | void *get_symbol(const std::string &name) const noexcept override{ 163 | if(!is_valid()) return nullptr; 164 | 165 | auto sym = detail::get_symbol(m_handle, name.c_str()); 166 | if(!sym){ 167 | print_error("Error in get_symbol: {}", detail::get_error()); 168 | } 169 | 170 | return sym; 171 | } 172 | 173 | private: 174 | void import_entities(){ 175 | for(auto &&sym : m_symbols){ 176 | auto readable = demangle(sym); 177 | 178 | constexpr std::string_view exportFnName = "reflpp::detail::function_export"; 179 | const auto exported_fn_res = readable.find(exportFnName); 180 | if(exported_fn_res != std::string::npos){ 181 | auto ptr = get_symbol(sym); 182 | auto f = reinterpret_cast(ptr); 183 | assert(f); 184 | auto fn = m_fns.emplace_back(f()); 185 | continue; 186 | } 187 | 188 | constexpr std::string_view exportTypeName = "reflpp::detail::type_export"; 189 | const auto exported_type_res = readable.find(exportTypeName); 190 | if(exported_type_res != std::string::npos){ 191 | auto ptr = get_symbol(sym); 192 | auto f = reinterpret_cast(ptr); 193 | assert(f); 194 | auto type = m_types.emplace_back(f()); 195 | refl::detail::register_type(type); 196 | continue; 197 | } 198 | } 199 | } 200 | 201 | void reset(detail::lib_handle handle = nullptr){ 202 | auto old_handle = std::exchange(m_handle, handle); 203 | 204 | if(old_handle && !detail::close_library(old_handle)){ 205 | print_error("Error in close_library: {}", detail::get_error()); 206 | } 207 | } 208 | 209 | detail::lib_handle m_handle; 210 | std::vector m_symbols; 211 | std::vector m_types; 212 | std::vector m_fns; 213 | }; 214 | 215 | class plugin_loader{ 216 | public: 217 | plugin_loader(): m_self(self_t{}){} 218 | 219 | const dynamic_library *load(const fs::path &path){ 220 | if(!fs::exists(path)){ 221 | print_error("Plugin path '{}' does not exist", path.u8string()); 222 | return nullptr; 223 | } 224 | else if(!fs::is_regular_file(path)){ 225 | print_error("Plugin path '{}' is not a file", path.u8string()); 226 | return nullptr; 227 | } 228 | 229 | auto abs_path = fs::absolute(path); 230 | auto abs_path_utf8 = abs_path.u8string(); 231 | 232 | auto res = m_libraries.find(abs_path_utf8); 233 | if(res != m_libraries.end()){ 234 | return &res->second; 235 | } 236 | 237 | auto emplace_res = m_libraries.try_emplace(abs_path_utf8, abs_path); 238 | if(!emplace_res.second){ 239 | print_error("Internal error in std::unordered_map::try_emplace"); 240 | return nullptr; 241 | } 242 | 243 | return &emplace_res.first->second; 244 | } 245 | 246 | const dynamic_library *self() const noexcept{ 247 | return &m_self; 248 | } 249 | 250 | private: 251 | dynamic_library m_self; 252 | std::unordered_map m_libraries; 253 | }; 254 | 255 | static plugin_loader loader; 256 | } 257 | 258 | const library *plugin::load(const fs::path &path){ 259 | return loader.load(path); 260 | } 261 | 262 | const library *plugin::self(){ 263 | return loader.self(); 264 | } 265 | 266 | std::vector plugin::nearby_plugins(){ 267 | namespace fs = std::filesystem; 268 | 269 | auto exe_dir = dll::program_location().parent_path(); 270 | 271 | std::vector ret; 272 | 273 | for( 274 | auto &&entry : 275 | fs::directory_iterator(exe_dir, fs::directory_options::skip_permission_denied) 276 | ){ 277 | if(!entry.is_regular_file()) continue; 278 | 279 | auto path = entry.path(); 280 | 281 | if(path.extension() == dll::shared_library::suffix()){ 282 | ret.emplace_back(std::move(path)); 283 | } 284 | } 285 | 286 | return ret; 287 | } 288 | -------------------------------------------------------------------------------- /include/metacpp/ast.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Meta C++ Tool and Library 3 | * Copyright (C) 2022 Keith Hammond 4 | * 5 | * This Source Code Form is subject to the terms of the Mozilla Public 6 | * License, v. 2.0. If a copy of the MPL was not distributed with this 7 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef METACPP_AST_HPP 11 | #define METACPP_AST_HPP 1 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "metacpp/config.hpp" 19 | 20 | /** 21 | * @defgroup Ast AST parsing utilities. 22 | * @{ 23 | */ 24 | 25 | namespace astpp{ 26 | class attribute{ 27 | public: 28 | attribute(std::string scope_, std::string name_, std::vector args_ = {}) noexcept 29 | : m_scope(std::move(scope_)) 30 | , m_name(std::move(name_)) 31 | , m_args(std::move(args_)) 32 | {} 33 | 34 | attribute(std::string name_, std::vector args_ = {}) noexcept 35 | : attribute({}, std::move(name_), std::move(args_)) 36 | {} 37 | 38 | attribute(attribute&&) noexcept = default; 39 | 40 | attribute &operator=(attribute&&) noexcept = default; 41 | 42 | const std::string &scope() const noexcept{ return m_scope; } 43 | const std::string &name() const noexcept{ return m_name; } 44 | const std::vector &args() const noexcept{ return m_args; } 45 | 46 | std::string str() const noexcept{ 47 | std::string ret = has_scope() ? m_scope + "::" : ""; 48 | ret += m_name; 49 | if(has_args()){ 50 | ret += "(" + m_args[0]; 51 | for(std::size_t i = 1; i < m_args.size(); i++){ 52 | ret += ", " + m_args[i]; 53 | } 54 | ret += ")"; 55 | } 56 | return ret; 57 | } 58 | 59 | bool has_scope() const noexcept{ return !m_scope.empty(); } 60 | bool has_args() const noexcept{ return !m_args.empty(); } 61 | 62 | private: 63 | std::string m_scope, m_name; 64 | std::vector m_args; 65 | }; 66 | 67 | enum class entity_kind{ 68 | class_, enum_, function, namespace_, type, type_alias, 69 | 70 | template_param, 71 | 72 | class_specialization, 73 | class_base, 74 | class_constructor, 75 | class_destructor, 76 | class_member, 77 | class_method, 78 | 79 | enum_value 80 | }; 81 | 82 | enum class access_kind{ 83 | public_, protected_, private_ 84 | }; 85 | 86 | class namespace_info; 87 | 88 | struct entity_info{ 89 | entity_info() = default; 90 | 91 | entity_info(const entity_info&) = delete; 92 | entity_info(entity_info &&other) 93 | : name(std::move(other.name)) 94 | , attributes(std::move(other.attributes)) 95 | {} 96 | 97 | entity_info &operator=(const entity_info&) = delete; 98 | 99 | entity_info &operator=(entity_info &&other) noexcept{ 100 | if(this != &other){ 101 | name = std::move(other.name); 102 | attributes = std::move(other.attributes); 103 | } 104 | 105 | return *this; 106 | } 107 | 108 | virtual ~entity_info() = default; 109 | 110 | virtual entity_kind kind() const noexcept = 0; 111 | 112 | std::string name; 113 | std::vector attributes; 114 | namespace_info *ns; 115 | }; 116 | 117 | struct type_info: entity_info{ 118 | entity_kind kind() const noexcept override{ return entity_kind::type; } 119 | }; 120 | 121 | struct function_info: entity_info{ 122 | entity_kind kind() const noexcept override{ return entity_kind::function; } 123 | 124 | std::string type; 125 | std::string result_type; 126 | std::vector param_types, param_names; 127 | }; 128 | 129 | struct class_member_info: entity_info{ 130 | entity_kind kind() const noexcept override{ return entity_kind::class_member; } 131 | 132 | std::string type; 133 | bool is_accessable; 134 | }; 135 | 136 | struct class_method_info: entity_info{ 137 | entity_kind kind() const noexcept override{ return entity_kind::class_method; } 138 | 139 | std::size_t index; 140 | 141 | bool is_static; 142 | bool is_const; 143 | bool is_virtual; 144 | bool is_pure_virtual; 145 | bool is_defaulted; 146 | bool is_noexcept; 147 | bool is_accessable; 148 | 149 | std::string result_type; 150 | std::vector param_types, param_names; 151 | }; 152 | 153 | enum class constructor_kind{ 154 | move, copy, default_, converting, generic 155 | }; 156 | 157 | struct class_constructor_info: entity_info{ 158 | entity_kind kind() const noexcept override{ return entity_kind::class_destructor; } 159 | 160 | bool is_noexcept; 161 | bool is_accessable; 162 | 163 | enum constructor_kind constructor_kind; 164 | std::vector param_types, param_names; 165 | }; 166 | 167 | struct class_destructor_info: entity_info{ 168 | entity_kind kind() const noexcept override{ return entity_kind::class_destructor; } 169 | 170 | bool is_override; 171 | }; 172 | 173 | struct template_param_info: entity_info{ 174 | entity_kind kind() const noexcept override{ return entity_kind::template_param; } 175 | 176 | std::string declarator; 177 | std::string default_value; 178 | bool is_variadic = false; 179 | }; 180 | 181 | struct class_base_info: entity_info{ 182 | entity_kind kind() const noexcept override{ return entity_kind::class_base; } 183 | 184 | access_kind access; 185 | bool is_variadic = false; 186 | }; 187 | 188 | struct class_info: entity_info{ 189 | entity_kind kind() const noexcept override{ return entity_kind::class_; } 190 | 191 | bool is_abstract; 192 | bool is_template; 193 | bool is_specialization = false; 194 | 195 | std::vector bases; 196 | std::unordered_map> methods; 197 | std::vector members; 198 | std::unordered_map classes; 199 | std::vector ctors; 200 | std::vector template_params; 201 | std::vector template_args; 202 | const class_destructor_info *dtor = nullptr; 203 | }; 204 | 205 | struct class_specialization: entity_info{ 206 | entity_kind kind() const noexcept override{ return entity_kind::class_specialization; } 207 | 208 | class_info *cls; 209 | std::vector template_args; 210 | }; 211 | 212 | struct enum_value_info: entity_info{ 213 | entity_kind kind() const noexcept override{ return entity_kind::enum_value; } 214 | 215 | std::string name; 216 | std::uint64_t value; 217 | }; 218 | 219 | struct enum_info: entity_info{ 220 | entity_kind kind() const noexcept override{ return entity_kind::enum_; } 221 | 222 | bool is_scoped; 223 | 224 | std::vector values; 225 | }; 226 | 227 | struct type_alias_info: entity_info{ 228 | entity_kind kind() const noexcept override{ return entity_kind::type_alias; } 229 | 230 | std::string aliased; 231 | }; 232 | 233 | struct namespace_info: entity_info{ 234 | namespace_info() = default; 235 | 236 | namespace_info(namespace_info&&) noexcept = default; 237 | namespace_info(const namespace_info&) = delete; 238 | 239 | namespace_info &operator=(namespace_info&&) noexcept = default; 240 | 241 | entity_kind kind() const noexcept override{ return entity_kind::namespace_; } 242 | 243 | std::unordered_map classes; 244 | std::unordered_map enums; 245 | std::unordered_map> functions; 246 | std::unordered_map namespaces; 247 | std::unordered_map aliases; 248 | }; 249 | 250 | using entity = std::variant; 251 | 252 | struct info_map{ 253 | namespace_info global; 254 | std::unordered_map namespaces; 255 | std::vector> storage; 256 | }; 257 | 258 | enum class cppstd{ 259 | _11, gnu11, _14, gnu14, _17, gnu17, _20, gnu20, 260 | }; 261 | 262 | class compile_info{ 263 | public: 264 | explicit compile_info(const std::filesystem::path &build_dir); 265 | ~compile_info(); 266 | 267 | std::vector all_options(const std::vector &add_args = {}) const; 268 | std::vector file_options(const std::filesystem::path &path, const std::vector &add_args = {}) const; 269 | 270 | std::vector all_include_dirs() const; 271 | std::vector file_include_dirs(const std::filesystem::path &path) const; 272 | 273 | struct data; 274 | std::unique_ptr impl; 275 | }; 276 | 277 | info_map parse( 278 | const std::filesystem::path &path, 279 | const compile_info &info, 280 | std::vector cmd_args = {}, 281 | bool verbose 282 | #ifndef NDEBUG 283 | = true 284 | #else 285 | = false 286 | #endif 287 | ); 288 | 289 | info_map parse( 290 | const std::filesystem::path &path, 291 | std::vector compile_args, 292 | bool verbose 293 | #ifndef NDEBUG 294 | = true 295 | #else 296 | = false 297 | #endif 298 | ); 299 | 300 | std::string compiler_version(); 301 | } 302 | 303 | #ifndef METACPP_NO_NAMESPACE_ALIAS 304 | namespace METACPP_AST_NAMESPACE = astpp; 305 | #endif 306 | 307 | /** 308 | * @} 309 | */ 310 | 311 | #endif // !METACPP_AST_HPP 312 | -------------------------------------------------------------------------------- /refl/tool.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Meta C++ Tool and Library 3 | * Copyright (C) 2022 Keith Hammond 4 | * 5 | * This Source Code Form is subject to the terms of the Mozilla Public 6 | * License, v. 2.0. If a copy of the MPL was not distributed with this 7 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "fmt/format.h" 16 | 17 | #include "metacpp/config.hpp" 18 | #include "make_meta.hpp" 19 | 20 | namespace fs = std::filesystem; 21 | 22 | std::string make_function_refl(const ast::function_info &fn){ 23 | std::string full_name = fn.name; 24 | std::string param_names_arr, param_types_arr, param_types_str; 25 | 26 | constexpr std::string_view operator_prefix = "::operator"; 27 | 28 | // TODO: handle operator overloads 29 | if(std::string_view(full_name).substr(0, operator_prefix.size()) == operator_prefix){ 30 | return ""; 31 | } 32 | 33 | if(!fn.param_types.empty()){ 34 | for(std::size_t i = 0; i < fn.param_types.size(); i++){ 35 | auto &¶m_name = fn.param_names[i]; 36 | auto &¶m_type = fn.param_types[i]; 37 | 38 | param_names_arr += fmt::format(", \"{}\"", param_name); 39 | param_types_arr += fmt::format(", reflpp::reflect<{}>()", param_type); 40 | param_types_str += fmt::format(", {}", param_type); 41 | } 42 | 43 | param_names_arr.erase(0, 2); 44 | param_types_arr.erase(0, 2); 45 | param_types_str.erase(0, 2); 46 | 47 | param_names_arr = fmt::format( 48 | "\t" "\t" "const char *const param_name_arr[{}] = {{ {} }};\n" 49 | "\t" "\t" "std::string_view param_name(std::size_t idx) const noexcept override{{ return idx >= num_params() ? \"\" : param_name_arr[idx]; }}\n", 50 | fn.param_names.size(), 51 | param_names_arr 52 | ); 53 | 54 | param_types_arr = fmt::format( 55 | "\t" "\t" "const reflpp::type_info param_type_arr[{}] = {{ {} }};\n" 56 | "\t" "\t" "reflpp::type_info param_type(std::size_t idx) const noexcept override{{ return idx >= num_params() ? nullptr : param_type_arr[idx]; }}\n", 57 | fn.param_types.size(), 58 | param_types_arr 59 | ); 60 | } 61 | else{ 62 | param_names_arr = "\t" "\t" "std::string_view param_name(std::size_t) const noexcept override{{ return \"\"; }}\n"; 63 | param_types_arr = "\t" "\t" "reflpp::type_info param_type(std::size_t) const noexcept override{{ return nullptr; }}\n"; 64 | } 65 | 66 | std::string fn_val = fmt::format( 67 | "static_cast<{}(*)({})>(&{})", 68 | fn.result_type, param_types_str, full_name 69 | ); 70 | 71 | return fmt::format( 72 | "template<> REFLCPP_EXPORT_SYMBOL reflpp::function_info reflpp::detail::function_export<({5})>(){{\n" 73 | "\t" "struct function_info_impl: detail::function_info_helper{{\n" 74 | "\t" "\t" "std::string_view name() const noexcept override{{ return \"{0}\"; }}\n" 75 | "\t" "\t" "const reflpp::type_info result_type_val = reflpp::reflect<{1}>();\n" 76 | "\t" "\t" "reflpp::type_info result_type() const noexcept override{{ return result_type_val; }}\n" 77 | "\t" "\t" "std::size_t num_params() const noexcept override{{ return {2}; }}\n" 78 | "{3}" 79 | "{4}" 80 | "\t" "}} static ret;\n" 81 | "\t" "return &ret;\n" 82 | "}}\n", 83 | full_name, 84 | fn.result_type, 85 | fn.param_types.size(), 86 | param_names_arr, 87 | param_types_arr, 88 | fn_val 89 | ); 90 | } 91 | 92 | std::string make_namespace_refl(const ast::namespace_info &ns, std::string &ctor_calls){ 93 | std::string output; 94 | 95 | for(auto &&fns : ns.functions){ 96 | for(auto &&fn : fns.second){ 97 | output += fmt::format("{}\n", make_function_refl(*fn)); 98 | } 99 | } 100 | 101 | for(auto &&enm : ns.enums){ 102 | output += fmt::format( 103 | "template<> REFLCPP_EXPORT_SYMBOL reflpp::type_info reflpp::detail::type_export<{0}>(){{\n" 104 | "\t" "static const auto ret = reflpp::detail::reflect_info<{0}>::reflect();\n" 105 | "\t" "return ret;\n" 106 | "}}\n" 107 | "\n", 108 | enm.second->name 109 | ); 110 | 111 | ctor_calls += fmt::format( 112 | "\t" "reflpp::detail::type_export<{}>();\n", 113 | enm.second->name 114 | ); 115 | } 116 | 117 | for(auto &&cls : ns.classes){ 118 | if(cls.second->is_template) continue; 119 | 120 | output += fmt::format( 121 | "template<> REFLCPP_EXPORT_SYMBOL reflpp::type_info reflpp::detail::type_export<{0}>(){{\n" 122 | "\t" "static const auto ret = reflpp::detail::reflect_info<{0}>::reflect();\n" 123 | "\t" "return ret;\n" 124 | "}}\n" 125 | "\n", 126 | cls.second->name 127 | ); 128 | 129 | ctor_calls += fmt::format( 130 | "\t" "reflpp::detail::type_export<{}>();\n", 131 | cls.second->name 132 | ); 133 | } 134 | 135 | for(auto &&inner : ns.namespaces){ 136 | output += make_namespace_refl(*inner.second, ctor_calls); 137 | } 138 | 139 | return output; 140 | } 141 | 142 | void print_version(){ 143 | fmt::print( 144 | METACPP_VERSION_STR "\n" 145 | "Compiler: {}\n", 146 | ast::compiler_version() 147 | ); 148 | } 149 | 150 | void print_usage(const char *argv0, std::FILE *out = stdout){ 151 | fmt::print(out, "Usage: {} [-v|--version] [-d|--debug] [-o ] header [other-headers ..]\n", argv0); 152 | } 153 | 154 | int main(int argc, char *argv[]){ 155 | if(argc < 3){ 156 | print_usage(argv[0], stderr); 157 | return EXIT_FAILURE; 158 | } 159 | 160 | bool verbose 161 | #ifndef NDEBUG 162 | = true; 163 | #else 164 | = false; 165 | #endif 166 | 167 | fs::path output_dir = fs::path(argv[0]).parent_path(); 168 | std::string output_dir_utf8; 169 | 170 | fs::path build_dir; 171 | std::string build_dir_utf8; 172 | 173 | std::vector headers; 174 | 175 | headers.reserve(argc - 2); // we know argc >= 3 176 | 177 | bool version_printed = false; 178 | 179 | int argi = 1; 180 | for(; argi < argc; argi++){ 181 | std::string_view arg = argv[argi]; 182 | 183 | if(arg == "-v" || arg == "--version"){ 184 | if(!version_printed){ 185 | print_version(); 186 | version_printed = true; 187 | } 188 | } 189 | else if(arg == "-o"){ 190 | ++argi; 191 | if(argi == argc){ 192 | print_usage(argv[0], stderr); 193 | return EXIT_FAILURE; 194 | } 195 | 196 | output_dir = fs::path(argv[argi]); 197 | output_dir_utf8 = output_dir.string(); 198 | 199 | if(!fs::exists(output_dir)){ 200 | if(!fs::create_directory(output_dir)){ 201 | fmt::print(stderr, "could not create directory '{}'\n", output_dir_utf8); 202 | return EXIT_FAILURE; 203 | } 204 | } 205 | else if(!fs::is_directory(output_dir)){ 206 | fmt::print(stderr, "'{}' is not a directory\n", output_dir_utf8); 207 | return EXIT_FAILURE; 208 | } 209 | } 210 | else if(arg == "-d" || arg == "--debug"){ 211 | verbose = true; 212 | 213 | if(!version_printed){ 214 | print_version(); 215 | version_printed = true; 216 | } 217 | } 218 | else if(arg == "--"){ 219 | // rest of args are for compiler 220 | ++argi; 221 | break; 222 | } 223 | else if(build_dir.empty()){ 224 | build_dir = arg; 225 | build_dir_utf8 = build_dir.string(); 226 | 227 | if(!fs::exists(build_dir)){ 228 | fmt::print(stderr, "build directory '{}' does not exist\n", build_dir_utf8); 229 | return EXIT_FAILURE; 230 | } 231 | else if(!fs::is_directory(build_dir)){ 232 | fmt::print(stderr, "'{}' is not a build directory\n", build_dir_utf8); 233 | return EXIT_FAILURE; 234 | } 235 | } 236 | else{ 237 | fs::path header = arg; 238 | auto header_utf8 = header.u8string(); 239 | 240 | if(!fs::exists(header)){ 241 | fmt::print(stderr, "header '{}' does not exist\n", (const char*)header_utf8.c_str()); 242 | return EXIT_FAILURE; 243 | } 244 | else if(!fs::is_regular_file(header)){ 245 | fmt::print(stderr, "'{}' is not a header file\n", (const char*)header_utf8.c_str()); 246 | return EXIT_FAILURE; 247 | } 248 | 249 | headers.emplace_back(std::move(header)); 250 | } 251 | } 252 | 253 | if(verbose && !version_printed){ 254 | print_version(); 255 | version_printed = true; 256 | } 257 | 258 | if(build_dir.empty()){ 259 | if(version_printed){ 260 | return EXIT_SUCCESS; 261 | } 262 | else{ 263 | fmt::print(stderr, "no build directory specified\n"); 264 | return EXIT_FAILURE; 265 | } 266 | } 267 | else if(headers.empty()){ 268 | if(version_printed){ 269 | return EXIT_SUCCESS; 270 | } 271 | else{ 272 | fmt::print(stderr, "no header files passed\n"); 273 | return EXIT_FAILURE; 274 | } 275 | } 276 | 277 | auto compile_info = ast::compile_info(build_dir); 278 | 279 | const auto include_dirs = compile_info.all_include_dirs(); 280 | 281 | std::vector> m_futs; 282 | m_futs.reserve(headers.size()); 283 | 284 | std::vector compile_args; 285 | 286 | if(argi < argc){ 287 | compile_args.reserve(argc - argi); 288 | 289 | std::transform( 290 | argv + argi, argv + argc, 291 | std::back_inserter(compile_args), 292 | [](const char *arg) -> std::string{ return {arg}; } 293 | ); 294 | } 295 | 296 | for(const auto &arg : compile_args){ 297 | fmt::print(stderr, "Compiler arg: {}\n", arg); 298 | } 299 | 300 | for(const auto &header : headers){ 301 | m_futs.emplace_back( 302 | std::async(std::launch::async, [&header, &compile_args, verbose, output_dir, &include_dirs, &compile_info]{ 303 | const auto abs_header = fs::absolute(header).string(); 304 | 305 | auto info = ast::parse(header, compile_info, compile_args, verbose); 306 | 307 | auto file_output_dir = output_dir; 308 | 309 | for(auto &&dir : include_dirs){ 310 | const auto abs_dir = fs::absolute(dir).string(); 311 | const auto initial_header = std::string_view(abs_header).substr(0, abs_dir.size()); 312 | 313 | if(initial_header == abs_dir){ 314 | const auto abs_header_dir = fs::path(abs_header).parent_path().string(); 315 | 316 | const auto rel_header = std::string_view(abs_header_dir).substr(abs_dir.size() + 1); 317 | 318 | file_output_dir = output_dir / rel_header; 319 | 320 | break; 321 | } 322 | } 323 | 324 | const auto header_file = header.filename(); 325 | 326 | auto out_header_path = file_output_dir / header_file; 327 | 328 | out_header_path.replace_extension(fmt::format(".meta{}", header_file.extension().string())); 329 | 330 | auto out_header_path_utf8 = out_header_path.u8string(); 331 | 332 | auto out_source_path = file_output_dir / header_file; 333 | out_source_path += ".refl.cpp"; 334 | auto out_source_path_utf8 = out_source_path.u8string(); 335 | 336 | std::string ctor_calls; 337 | auto namespace_refl = make_namespace_refl(info.global, ctor_calls); 338 | 339 | std::string out_source = fmt::format( 340 | "#define REFLCPP_IMPLEMENTATION\n" 341 | "#include \"{}\"\n" 342 | "#include \"metacpp/refl.hpp\"\n" 343 | "\n" 344 | "{}" 345 | "\n" 346 | "__attribute__((constructor))\n" 347 | "static void reflpp_load_type_info(){{\n" 348 | "{}" 349 | "}}", 350 | fs::absolute(out_header_path).string(), 351 | namespace_refl, 352 | ctor_calls 353 | ); 354 | 355 | std::string out_header = fmt::format( 356 | "#pragma once\n" 357 | "\n" 358 | "#include \"{}\"\n" 359 | "#include \"metacpp/meta.hpp\"\n" 360 | "\n" 361 | "{}", 362 | fs::absolute(header).string(), 363 | make_namespace_meta(info.global) 364 | ); 365 | 366 | auto out_header_dir = out_source_path.parent_path(); 367 | auto out_header_dir_utf8 = out_header_dir.u8string(); 368 | 369 | if(!fs::exists(out_header_dir) && !fs::create_directories(out_header_dir)){ 370 | fmt::print(stderr, "could not create directory '{}'\n", out_header_dir.string()); 371 | std::exit(EXIT_FAILURE); 372 | } 373 | 374 | { 375 | std::ofstream out_source_file(out_source_path); 376 | if(!out_source_file ){ 377 | fmt::print(stderr, "could not create output file '{}'\n", out_source_path.string()); 378 | std::exit(EXIT_FAILURE); 379 | } 380 | 381 | out_source_file << out_source; 382 | } 383 | 384 | { 385 | std::ofstream out_header_file(out_header_path); 386 | if(!out_header_file ){ 387 | fmt::print(stderr, "could not create output file '{}'\n", out_header_path.string()); 388 | std::exit(EXIT_FAILURE); 389 | } 390 | 391 | out_header_file << out_header; 392 | } 393 | }) 394 | ); 395 | } 396 | 397 | for(auto &&fut : m_futs){ 398 | fut.get(); 399 | } 400 | 401 | return EXIT_SUCCESS; 402 | } 403 | -------------------------------------------------------------------------------- /ast/clang.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Meta C++ Tool and Library 3 | * Copyright (C) 2022 Keith Hammond 4 | * 5 | * This Source Code Form is subject to the terms of the Mozilla Public 6 | * License, v. 2.0. If a copy of the MPL was not distributed with this 7 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef METACPP_AST_CLANG_HPP 11 | #define METACPP_AST_CLANG_HPP 1 12 | 13 | #include "metacpp/ast.hpp" 14 | 15 | #include "clang-c/Index.h" 16 | #include "clang-c/CXCompilationDatabase.h" 17 | 18 | #include "fmt/format.h" 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | namespace astpp::clang{ 26 | namespace fs = std::filesystem; 27 | 28 | template 29 | class handle{ 30 | public: 31 | handle(const handle&) = delete; 32 | 33 | handle(handle &&other) 34 | : m_handle(other.m_handle) 35 | { 36 | other.m_handle = nullptr; 37 | } 38 | 39 | virtual ~handle(){ 40 | if(m_handle) DestroyFn(m_handle); 41 | } 42 | 43 | handle &operator=(const handle&) = delete; 44 | 45 | handle &operator=(handle &&other) noexcept{ 46 | if(this != &other){ 47 | auto old_handle = std::exchange(m_handle, other.m_handle); 48 | 49 | other.m_handle = nullptr; 50 | 51 | if(old_handle){ 52 | DestroyFn(old_handle); 53 | } 54 | } 55 | 56 | return *this; 57 | } 58 | 59 | operator T() const noexcept{ return m_handle; } 60 | operator bool() const noexcept{ return m_handle != nullptr; } 61 | 62 | protected: 63 | handle(T handle_) noexcept 64 | : m_handle(handle_){} 65 | 66 | void set_handle(T handle_){ 67 | auto old_handle = std::exchange(m_handle, handle_); 68 | if(old_handle) DestroyFn(old_handle); 69 | } 70 | 71 | private: 72 | T m_handle; 73 | }; 74 | 75 | class index: public handle{ 76 | public: 77 | index(bool excludeDeclarationsFromPCH, bool displayDiagnostics) 78 | : handle(clang_createIndex(excludeDeclarationsFromPCH, displayDiagnostics)) 79 | {} 80 | 81 | index() 82 | #ifndef NDEBUG 83 | : index(false, true) 84 | #else 85 | : index(false, false) 86 | #endif 87 | {} 88 | }; 89 | 90 | namespace detail{ 91 | template 92 | void null_destroy(T){} 93 | 94 | inline std::string convert_str(CXString str){ 95 | std::string ret = clang_getCString(str); 96 | clang_disposeString(str); 97 | return ret; 98 | } 99 | } 100 | 101 | class token{ 102 | public: 103 | token(CXTranslationUnit tu, CXToken tok) noexcept 104 | : m_tu(tu), m_tok(tok){} 105 | 106 | token() = default; 107 | 108 | token(const token&) = default; 109 | 110 | operator CXToken() const noexcept{ return m_tok; } 111 | 112 | bool is_valid() const noexcept{ return m_tu; } 113 | 114 | CXTokenKind kind() const noexcept{ return clang_getTokenKind(m_tok); } 115 | 116 | std::string str() const noexcept{ 117 | return is_valid() ? detail::convert_str(clang_getTokenSpelling(m_tu, m_tok)) : ""; 118 | } 119 | 120 | private: 121 | CXTranslationUnit m_tu = nullptr; 122 | CXToken m_tok; 123 | 124 | friend class token_iterator; 125 | }; 126 | 127 | class token_iterator{ 128 | public: 129 | token_iterator() = default; 130 | 131 | token_iterator(const token_iterator&) = default; 132 | 133 | token_iterator &operator=(const token_iterator&) = default; 134 | 135 | bool is_valid() const noexcept{ return m_it; } 136 | 137 | const token &operator*() const noexcept{ 138 | return m_val; 139 | } 140 | 141 | const token *operator->() const noexcept{ 142 | return &m_val; 143 | } 144 | 145 | token_iterator &operator++() noexcept{ 146 | if(m_it == m_end) return *this; 147 | ++m_it; 148 | m_val = token(m_tu, *m_it); 149 | return *this; 150 | } 151 | 152 | token_iterator &operator--() noexcept{ 153 | if(m_it == m_begin) return *this; 154 | --m_it; 155 | m_val = token(m_tu, *m_it); 156 | return *this; 157 | } 158 | 159 | bool operator==(const token_iterator &other) const noexcept{ 160 | return m_tu == other.m_tu && m_it == other.m_it; 161 | } 162 | 163 | bool operator!=(const token_iterator &other) const noexcept{ 164 | return m_tu != other.m_tu || m_it != other.m_it; 165 | } 166 | 167 | using difference_type = long; 168 | using value_type = token; 169 | using pointer = const token*; 170 | using reference = const token&; 171 | using iterator_category = std::bidirectional_iterator_tag; 172 | 173 | private: 174 | token_iterator(CXTranslationUnit tu, CXToken *it, CXToken *begin_, CXToken *end_) 175 | : m_tu(tu) 176 | , m_it(it) 177 | , m_begin(begin_) 178 | , m_end(end_) 179 | , m_val(it == end_ ? token() : token(tu, *m_it)) 180 | {} 181 | 182 | CXTranslationUnit m_tu = nullptr; 183 | CXToken *m_it = nullptr, *m_begin = nullptr, *m_end = nullptr; 184 | token m_val; 185 | 186 | friend class tokens; 187 | }; 188 | 189 | class tokens{ 190 | public: 191 | tokens(tokens &&other) noexcept 192 | : m_tu(std::exchange(other.m_tu, nullptr)) 193 | , m_toks(std::exchange(other.m_toks, nullptr)) 194 | , m_num_toks(std::exchange(other.m_num_toks, 0)) 195 | {} 196 | 197 | tokens(const tokens&) = delete; 198 | 199 | ~tokens(){ 200 | clang_disposeTokens(m_tu, m_toks, m_num_toks); 201 | } 202 | 203 | tokens &operator=(tokens &&other) noexcept{ 204 | if(this != &other){ 205 | if(m_toks) clang_disposeTokens(m_tu, m_toks, m_num_toks); 206 | 207 | m_tu = std::exchange(other.m_tu, nullptr); 208 | m_toks = std::exchange(other.m_toks, nullptr); 209 | m_num_toks = std::exchange(other.m_num_toks, 0); 210 | } 211 | 212 | return *this; 213 | } 214 | 215 | tokens &operator=(const tokens&) = delete; 216 | 217 | unsigned int num_tokens() const noexcept{ return m_num_toks; } 218 | 219 | bool empty() const noexcept{ return m_num_toks == 0; } 220 | 221 | std::size_t size() const noexcept{ return m_num_toks; } 222 | 223 | auto begin() const noexcept{ 224 | return token_iterator(m_tu, m_toks, m_toks, m_toks + m_num_toks); 225 | } 226 | 227 | auto end() const noexcept{ 228 | const auto end_ptr = m_toks + m_num_toks; 229 | return token_iterator(m_tu, end_ptr, m_toks, end_ptr); 230 | } 231 | 232 | private: 233 | tokens(CXCursor c){ 234 | m_tu = clang_Cursor_getTranslationUnit(c); 235 | auto extent = clang_getCursorExtent(c); 236 | 237 | if(c.kind == CXCursor_FieldDecl){ 238 | CXSourceLocation loc = clang_getCursorLocation(c); 239 | while(--loc.int_data > 0){ 240 | auto tokp = clang_getToken(m_tu, loc); 241 | if(!tokp){ 242 | continue; 243 | } 244 | 245 | auto tok = clang::token(m_tu, *tokp); 246 | if(!tok.is_valid()){ 247 | break; 248 | } 249 | 250 | if(tok.str() == ";" || tok.str() == "{"){ 251 | ++loc.int_data; 252 | extent = clang_getRange(loc, clang_getRangeEnd(extent)); 253 | break; 254 | } 255 | } 256 | } 257 | 258 | clang_tokenize(m_tu, extent, &m_toks, &m_num_toks); 259 | } 260 | 261 | CXTranslationUnit m_tu = nullptr; 262 | CXToken *m_toks = nullptr; 263 | unsigned int m_num_toks = 0; 264 | 265 | friend class cursor; 266 | }; 267 | 268 | class cursor; 269 | 270 | class type{ 271 | public: 272 | type(CXType t) noexcept 273 | : m_handle(t){} 274 | 275 | type(const type&) noexcept = default; 276 | 277 | type &operator=(const type&) noexcept = default; 278 | 279 | operator CXType() const noexcept{ return m_handle; } 280 | 281 | bool is_valid() const noexcept{ return m_handle.kind != CXType_Invalid; } 282 | 283 | CXTypeKind kind() const noexcept{ return m_handle.kind; } 284 | 285 | std::string kind_spelling() const noexcept{ 286 | return detail::convert_str(clang_getTypeKindSpelling(kind())); 287 | } 288 | 289 | std::string spelling(){ 290 | return detail::convert_str(clang_getTypeSpelling(m_handle)); 291 | } 292 | 293 | cursor declaration() const noexcept; 294 | 295 | private: 296 | CXType m_handle; 297 | }; 298 | 299 | class cursor{ 300 | public: 301 | cursor(CXCursor c) noexcept 302 | : m_handle(c){} 303 | 304 | cursor(const cursor&) noexcept = default; 305 | 306 | cursor &operator=(const cursor&) noexcept = default; 307 | 308 | operator CXCursor() const noexcept{ return m_handle; } 309 | 310 | CXCursorKind kind() const noexcept{ return clang_getCursorKind(m_handle); } 311 | 312 | class type type() const noexcept{ return clang_getCursorType(m_handle); } 313 | 314 | class tokens tokens() const noexcept{ return clang::tokens(m_handle); } 315 | 316 | std::string display_name(){ 317 | return detail::convert_str(clang_getCursorDisplayName(m_handle)); 318 | } 319 | 320 | std::string spelling(){ 321 | return detail::convert_str(clang_getCursorSpelling(m_handle)); 322 | } 323 | 324 | std::string kind_spelling(){ 325 | return detail::convert_str(clang_getCursorKindSpelling(kind())); 326 | } 327 | 328 | bool is_null() const noexcept{ 329 | return clang_Cursor_isNull(m_handle); 330 | } 331 | 332 | bool is_valid() const noexcept{ 333 | return !clang_isInvalid(kind()); 334 | } 335 | 336 | bool is_class_decl() const noexcept{ 337 | return clang_getCursorKind(m_handle) == CXCursor_ClassDecl; 338 | } 339 | 340 | bool is_attribute() const noexcept{ 341 | return clang_isAttribute(clang_getCursorKind(m_handle)); 342 | } 343 | 344 | template 345 | void visit_children(F &&f, Args &&... args){ 346 | using namespace std::placeholders; 347 | 348 | auto f0 = std::bind(std::forward(f), _1, _2, std::forward(args)...); 349 | 350 | clang_visitChildren( 351 | m_handle, 352 | [](CXCursor c, CXCursor parent, CXClientData client_data){ 353 | auto &&f = *reinterpret_cast(client_data); 354 | f(cursor(c), cursor(parent)); 355 | return CXChildVisit_Continue; 356 | }, 357 | &f0 358 | ); 359 | } 360 | 361 | std::optional num_args() const noexcept{ 362 | auto n = clang_Cursor_getNumArguments(m_handle); 363 | if(n == -1){ 364 | return std::nullopt; 365 | } 366 | 367 | return std::size_t(n); 368 | } 369 | 370 | std::optional arg(std::size_t i) const noexcept{ 371 | auto num_args_opt = num_args(); 372 | if(!num_args_opt) return std::nullopt; 373 | 374 | auto &&n = *num_args_opt; 375 | if(i >= n) return std::nullopt; 376 | 377 | return clang_Cursor_getArgument(m_handle, i); 378 | } 379 | 380 | private: 381 | CXCursor m_handle; 382 | }; 383 | 384 | inline cursor type::declaration() const noexcept{ 385 | return clang_getTypeDeclaration(m_handle); 386 | } 387 | 388 | class compilation_database: public handle{ 389 | public: 390 | explicit compilation_database(const fs::path &build_dir) 391 | : handle(nullptr) 392 | { 393 | auto build_dir_utf8 = build_dir.u8string(); 394 | 395 | CXCompilationDatabase_Error db_err; 396 | auto comp_db = clang_CompilationDatabase_fromDirectory(build_dir_utf8.c_str(), &db_err); 397 | 398 | if(db_err != CXCompilationDatabase_NoError){ 399 | auto msg = fmt::format("Compilation database could not be loaded from directory '{}'", build_dir_utf8.c_str()); 400 | throw std::runtime_error(msg); 401 | } 402 | 403 | set_handle(comp_db); 404 | } 405 | 406 | compilation_database(compilation_database&&) = default; 407 | 408 | std::vector all_options(const std::vector &additional = {}) const{ 409 | auto cmds = clang_CompilationDatabase_getAllCompileCommands(*this); 410 | if(!cmds){ 411 | std::vector ret; 412 | ret.reserve(additional.size()); 413 | 414 | for(std::size_t i = 0; i < additional.size(); i++){ 415 | ret.emplace_back(additional[i]); 416 | } 417 | 418 | return ret; 419 | } 420 | 421 | auto num_commands = clang_CompileCommands_getSize(cmds); 422 | 423 | std::vector ret; 424 | ret.reserve(num_commands + additional.size()); 425 | 426 | for(unsigned int i = 0; i < num_commands; i++){ 427 | auto cmd = clang_CompileCommands_getCommand(cmds, i); 428 | if(!cmd){ 429 | continue; 430 | } 431 | 432 | auto num_args = clang_CompileCommand_getNumArgs(cmd); 433 | 434 | // always start from 1 and end 1 before the end 435 | // head is compiler executable 436 | // last is compiled file 437 | for(unsigned int j = 1; j < (num_args - 1); j++){ 438 | auto arg = clang::detail::convert_str(clang_CompileCommand_getArg(cmd, j)); 439 | ret.emplace_back(std::move(arg)); 440 | } 441 | } 442 | 443 | if(ret.back() == "--"){ 444 | ret.erase(ret.rbegin().base()); 445 | } 446 | 447 | clang_CompileCommands_dispose(cmds); 448 | 449 | for(std::size_t i = 0; i < additional.size(); i++){ 450 | ret.emplace_back(additional[i]); 451 | } 452 | 453 | return ret; 454 | } 455 | 456 | std::vector file_options(const fs::path &path, const std::vector &additional = {}) const{ 457 | auto abs_path = fs::absolute(path); 458 | auto abs_path_u8 = abs_path.u8string(); 459 | 460 | auto cmds = clang_CompilationDatabase_getCompileCommands(*this, (const char*)abs_path_u8.c_str()); 461 | if(!cmds){ 462 | std::vector ret; 463 | ret.reserve(additional.size()); 464 | 465 | for(std::size_t i = 0; i < additional.size(); i++){ 466 | ret.emplace_back(additional[i]); 467 | } 468 | 469 | return ret; 470 | } 471 | 472 | auto num_commands = clang_CompileCommands_getSize(cmds); 473 | 474 | std::vector ret; 475 | ret.reserve(num_commands); 476 | 477 | for(unsigned int i = 0; i < num_commands; i++){ 478 | auto cmd = clang_CompileCommands_getCommand(cmds, i); 479 | if(!cmd){ 480 | continue; 481 | } 482 | 483 | auto num_args = clang_CompileCommand_getNumArgs(cmd); 484 | 485 | // always start from 1 and end 1 before the end 486 | // head is compiler executable 487 | // last is compiled file 488 | for(unsigned int j = 1; j < (num_args - 1); j++){ 489 | auto arg = clang::detail::convert_str(clang_CompileCommand_getArg(cmd, j)); 490 | ret.emplace_back(std::move(arg)); 491 | } 492 | } 493 | 494 | if(ret.back() == "--"){ 495 | ret.erase(ret.rbegin().base()); 496 | } 497 | 498 | clang_CompileCommands_dispose(cmds); 499 | 500 | for(std::size_t i = 0; i < additional.size(); i++){ 501 | ret.emplace_back(additional[i]); 502 | } 503 | 504 | return ret; 505 | } 506 | }; 507 | 508 | class translation_unit: public handle{ 509 | public: 510 | translation_unit() 511 | : handle(nullptr) 512 | {} 513 | 514 | translation_unit(translation_unit&&) = default; 515 | 516 | translation_unit(CXIndex index, const fs::path &path, const std::vector &options = {}) 517 | : handle(nullptr) 518 | { 519 | if(!fs::exists(path)){ 520 | throw std::runtime_error(fmt::format("File does not exist: {}", path.string())); 521 | } 522 | 523 | std::vector option_cstrs; 524 | option_cstrs.reserve(options.size()); 525 | 526 | std::transform( 527 | options.begin(), options.end(), 528 | std::back_inserter(option_cstrs), 529 | [](const std::string &opt){ return opt.c_str(); } 530 | ); 531 | 532 | const auto num_options = static_cast(options.size()); 533 | 534 | auto path_utf8 = path.u8string(); 535 | 536 | CXTranslationUnit tu = nullptr; 537 | auto parse_err = clang_parseTranslationUnit2( 538 | index, path_utf8.c_str(), 539 | option_cstrs.data(), num_options, 540 | nullptr, 0, 541 | CXTranslationUnit_SkipFunctionBodies | CXTranslationUnit_KeepGoing, 542 | &tu 543 | ); 544 | 545 | switch(parse_err){ 546 | case CXError_Success: break; 547 | 548 | case CXError_Failure:{ 549 | std::string errMsg; 550 | if(tu){ 551 | const unsigned int num_diag = clang_getNumDiagnostics(tu); 552 | 553 | bool found_err = false; 554 | 555 | for(unsigned int i = 0; i < num_diag; i++){ 556 | CXDiagnostic diagnotic = clang_getDiagnostic(tu, i); 557 | auto err_str = 558 | clang::detail::convert_str( 559 | clang_formatDiagnostic(diagnotic, clang_defaultDiagnosticDisplayOptions()) 560 | ); 561 | 562 | errMsg += err_str; 563 | } 564 | } 565 | else{ 566 | errMsg = fmt::format("Failure in clang_parseTranslationUnit2 for '{}'", path_utf8); 567 | } 568 | 569 | throw std::runtime_error(errMsg); 570 | } 571 | 572 | case CXError_Crashed:{ 573 | throw std::runtime_error(fmt::format("libclang crashed while in clang_parseTranslationUnit2 for '{}'", path_utf8)); 574 | } 575 | 576 | case CXError_InvalidArguments:{ 577 | throw std::runtime_error(fmt::format("clang_parseTranslationUnit2 detected that it's arguments violate the function contract for '{}'", path_utf8)); 578 | } 579 | 580 | case CXError_ASTReadError:{ 581 | throw std::runtime_error(fmt::format("An AST deserialization error occurred for '{}'", path_utf8.c_str())); 582 | } 583 | 584 | default:{ 585 | throw std::runtime_error(fmt::format("Unknown error in clang_parseTranslationUnit2 for '{}'", path_utf8)); 586 | } 587 | } 588 | 589 | set_handle(tu); 590 | } 591 | 592 | cursor get_cursor() const noexcept{ 593 | return clang_getTranslationUnitCursor(*this); 594 | } 595 | }; 596 | 597 | inline std::string version(){ 598 | return detail::convert_str(clang_getClangVersion()); 599 | } 600 | } 601 | 602 | #endif // !METACPP_AST_CLANG_HPP 603 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /refl/make_meta.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Meta C++ Tool and Library 3 | * Copyright (C) 2022 Keith Hammond 4 | * 5 | * This Source Code Form is subject to the terms of the Mozilla Public 6 | * License, v. 2.0. If a copy of the MPL was not distributed with this 7 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef MAKE_META_HPP 11 | #define MAKE_META_HPP 1 12 | 13 | #include "metacpp/ast.hpp" 14 | 15 | #include "fmt/format.h" 16 | 17 | std::string make_function_meta( 18 | const ast::function_info &fn 19 | ){ 20 | std::string output; 21 | std::string param_types_str, params_member_str; 22 | 23 | for(std::size_t i = 0; i < fn.param_types.size(); i++){ 24 | auto &¶m_type = fn.param_types[i]; 25 | auto &¶m_name = fn.param_names[i]; 26 | 27 | params_member_str += fmt::format( 28 | ",\n" 29 | "\t" "\t" "metapp::param_info, metapp::value<{}>>", 30 | i 31 | ); 32 | 33 | param_types_str += fmt::format(", {}", param_type); 34 | } 35 | 36 | if(!param_types_str.empty()){ 37 | param_types_str.erase(0, 2); 38 | 39 | params_member_str.erase(0, 1); 40 | params_member_str += "\n\t"; 41 | } 42 | 43 | 44 | std::string full_name = fn.name; 45 | 46 | std::string fn_val = fmt::format( 47 | "static_cast<{}(*)({})>(&{})", 48 | fn.result_type, param_types_str, full_name 49 | ); 50 | 51 | for(std::size_t i = 0; i < fn.param_types.size(); i++){ 52 | auto &¶m_type = fn.param_types[i]; 53 | auto &¶m_name = fn.param_names[i]; 54 | 55 | const bool param_is_variadic = param_type.rfind("...") == (param_type.size() - 3); 56 | 57 | output += fmt::format( 58 | "template<> struct metapp::detail::param_info_data, {1}>{{\n" 59 | "\t" "using type = {2};\n" 60 | "\t" "static constexpr std::string_view name = \"{0}\";\n" 61 | "\t" "static constexpr bool is_variadic = {3};\n" 62 | "}};\n" 63 | "\n", 64 | param_name, 65 | i, 66 | param_is_variadic ? fmt::format("meta::types<{}>", param_type) : param_type, 67 | param_is_variadic, 68 | fn_val 69 | ); 70 | } 71 | 72 | return fmt::format( 73 | "{0}" 74 | "template<> struct metapp::detail::function_info_data<({5})>{{\n" 75 | "\t" "static constexpr std::string_view name = \"{1}\";\n" 76 | "\t" "using type = {2}(*)({3});\n" 77 | "\t" "static constexpr type ptr = {1};\n" 78 | "\t" "using result = {2};\n" 79 | "\t" "using params = metapp::types<{4}>;\n" 80 | "}};\n", 81 | output, 82 | full_name, 83 | fn.result_type, 84 | param_types_str, 85 | params_member_str, 86 | fn_val 87 | ); 88 | } 89 | 90 | std::string make_ctor_meta( 91 | std::string_view tmpl_params, 92 | std::string_view full_name, 93 | const ast::class_constructor_info &ctor, 94 | std::size_t idx 95 | ){ 96 | std::string output, param_types_str, params_member_str; 97 | 98 | std::size_t param_idx = 0; 99 | for(auto &¶m_type : ctor.param_types){ 100 | auto &¶m_name = ctor.param_names[param_idx]; 101 | 102 | param_types_str += fmt::format("{}, ", param_type); 103 | 104 | params_member_str += fmt::format( 105 | ",\n" 106 | "\t" "\t" "metapp::param_info>, metapp::value<{2}>>", 107 | full_name, idx, param_idx 108 | ); 109 | 110 | const bool param_is_variadic = param_type.rfind("...") == (param_type.size() - 3); 111 | 112 | output += fmt::format( 113 | "template<{0}> struct metapp::detail::param_info_data<{1}, {2}>{{\n" 114 | "\t" "using type = {3};\n" 115 | "\t" "static constexpr std::string_view name = \"{4}\";\n" 116 | "\t" "static constexpr bool is_variadic = {5};\n" 117 | "}};\n" 118 | "\n", 119 | tmpl_params, 120 | fmt::format("metapp::class_ctor_info<{}, metapp::value<{}>>", full_name, idx), 121 | param_idx, 122 | param_is_variadic ? fmt::format("metapp::types<{}>", param_type) : param_type, 123 | param_name, 124 | param_is_variadic 125 | ); 126 | 127 | ++param_idx; 128 | } 129 | 130 | if(!param_types_str.empty()){ 131 | param_types_str.erase(param_types_str.size() - 2); 132 | 133 | params_member_str.erase(0, 1); 134 | params_member_str += "\n\t"; 135 | } 136 | 137 | return fmt::format( 138 | "{8}" 139 | "template<{6}> struct metapp::detail::class_ctor_info_data<{0}, {1}>{{\n" 140 | "\t" "using params = metapp::types<{7}>;\n" 141 | "\t" "static constexpr std::size_t num_params = {2};\n" 142 | "\t" "static constexpr bool is_move_ctor = {3};\n" 143 | "\t" "static constexpr bool is_copy_ctor = {4};\n" 144 | "\t" "static constexpr bool is_default_ctor = {5};\n" 145 | "\t" "static constexpr bool is_accessable = {9};\n" 146 | "}};\n", 147 | full_name, 148 | idx, 149 | ctor.param_names.size(), 150 | ctor.constructor_kind == ast::constructor_kind::move, 151 | ctor.constructor_kind == ast::constructor_kind::copy, 152 | ctor.constructor_kind == ast::constructor_kind::default_, 153 | tmpl_params, 154 | params_member_str, 155 | output, 156 | ctor.is_accessable 157 | ); 158 | } 159 | 160 | std::string make_member_meta( 161 | std::string_view tmpl_params, 162 | std::string_view full_name, 163 | const ast::class_member_info &m, 164 | std::size_t idx 165 | ){ 166 | std::string output, ptr_str, attribs_member_str; 167 | 168 | if(!m.attributes.empty()){ 169 | std::size_t attrib_idx = 0; 170 | 171 | std::string full_ptr = fmt::format("metapp::value<&{}::{}>", full_name, m.name); 172 | 173 | for(auto &&attrib : m.attributes){ 174 | attribs_member_str += fmt::format( 175 | ",\n" 176 | "\t\t" "metapp::attrib_info<{0}, metapp::value<{1}>>", 177 | full_ptr, 178 | attrib_idx 179 | ); 180 | 181 | std::string args_member_str; 182 | 183 | std::size_t attrib_arg_idx = 0; 184 | 185 | for(auto &&arg : attrib.args()){ 186 | args_member_str += fmt::format( 187 | ",\n" 188 | "\t\t" "metapp::attrib_arg_info<{0}, metapp::value<{1}>, metapp::value<{2}>>", 189 | full_ptr, 190 | attrib_idx, 191 | attrib_arg_idx 192 | ); 193 | 194 | output += fmt::format( 195 | "template<{4}> struct metapp::detail::attrib_arg_info_data<{0}, {1}, {2}>{{\n" 196 | "\t" "static constexpr std::string_view value = R\"({3})\";\n" 197 | "}};\n" 198 | "\n", 199 | full_ptr, 200 | attrib_idx, 201 | attrib_arg_idx++, 202 | arg, 203 | tmpl_params 204 | ); 205 | } 206 | 207 | if(!args_member_str.empty()){ 208 | args_member_str.erase(0, 1); 209 | args_member_str += "\n\t"; 210 | } 211 | 212 | output += fmt::format( 213 | "template<{5}> struct metapp::detail::attrib_info_data<{0}, {1}>{{\n" 214 | "\t" "static constexpr std::string_view scope = \"{2}\";\n" 215 | "\t" "static constexpr std::string_view name = \"{3}\";\n" 216 | "\t" "using args = metapp::types<{4}>;\n" 217 | "}};\n" 218 | "\n", 219 | full_ptr, 220 | attrib_idx++, 221 | attrib.scope(), 222 | attrib.name(), 223 | args_member_str, 224 | tmpl_params 225 | ); 226 | } 227 | 228 | attribs_member_str.erase(0, 1); 229 | attribs_member_str += "\n\t"; 230 | } 231 | 232 | if(m.is_accessable){ 233 | ptr_str = fmt::format("\t" "static constexpr ptr_type ptr = &{}::{};\n", full_name, m.name); 234 | } 235 | else{ 236 | ptr_str = fmt::format("\t" "static constexpr metapp::inaccessible ptr = {{}};\n"); 237 | } 238 | 239 | return fmt::format( 240 | "{6}" 241 | "template<{4}> struct metapp::detail::class_member_info_data<{0}, {1}>{{\n" 242 | "\t" "using class_ = {0};\n" 243 | "\t" "using type = {3};\n" 244 | "\t" "using ptr_type = type ({0}::*);\n" 245 | "\t" "using attributes = metapp::types<{7}>;\n" 246 | "\t" "static constexpr std::string_view name = \"{2}\";\n" 247 | "{5}" 248 | "}};\n", 249 | full_name, idx, m.name, m.type, tmpl_params, ptr_str, output, attribs_member_str 250 | ); 251 | } 252 | 253 | std::string make_method_meta( 254 | std::string_view tmpl_params, 255 | std::string_view full_name, 256 | const ast::class_method_info &m, 257 | std::size_t idx 258 | ){ 259 | std::string param_metas_str, param_types_str, param_names_member_str, params_member_str; 260 | 261 | for(std::size_t i = 0; i < m.param_types.size(); i++){ 262 | std::string param_type = m.param_types[i]; 263 | auto &¶m_name = m.param_names[i]; 264 | 265 | const bool param_is_variadic = param_type.rfind("...") == (param_type.size() - 3); 266 | 267 | param_metas_str += fmt::format( 268 | "template<{5}> struct metapp::detail::class_method_param_info_data<{0}, {1}, {2}>{{\n" 269 | "\t" "static constexpr std::string_view name = \"{3}\";\n" 270 | "\t" "static constexpr bool is_variadic = {6};\n" 271 | "\t" "using type = {4};\n" 272 | "}};\n" 273 | "\n", 274 | full_name, idx, i, param_name, 275 | param_is_variadic ? fmt::format("metapp::types<{}>", param_type) : param_type, 276 | tmpl_params, 277 | param_is_variadic 278 | ); 279 | 280 | param_types_str += param_type; 281 | param_types_str += ", "; 282 | 283 | param_names_member_str += ",\n"; 284 | param_names_member_str += fmt::format("\t\t" "metapp::str(\"{}\")", param_name); 285 | 286 | params_member_str += ",\n"; 287 | params_member_str += fmt::format("\t\t" "metapp::class_method_param_info<{0}, metapp::value<{1}>, metapp::value<{2}>>", full_name, idx, i); 288 | } 289 | 290 | if(!param_types_str.empty()){ 291 | param_types_str.erase(param_types_str.size() - 2); 292 | 293 | param_names_member_str.erase(0, 1); 294 | param_names_member_str += "\n\t"; 295 | 296 | params_member_str.erase(0, 1); 297 | params_member_str += "\n\t"; 298 | } 299 | 300 | std::string ptr_str; 301 | 302 | if(m.is_accessable){ 303 | ptr_str = fmt::format("\t" "static constexpr ptr_type ptr = &{}::{};\n", full_name, m.name); 304 | } 305 | else{ 306 | ptr_str = fmt::format("\t" "static constexpr metapp::inaccessible ptr = {{}};\n"); 307 | } 308 | 309 | return fmt::format( 310 | "{7}" 311 | "template<{10}> struct metapp::detail::class_method_info_data<{0}, {1}>{{\n" 312 | "\t" "using ptr_type = {3}({12}*)({4}){6};\n" 313 | "\t" "using result = {9};\n" 314 | "\t" "using param_types = metapp::types<{4}>;\n" 315 | "\t" "using params = metapp::types<{8}>;\n" 316 | "\t" "static constexpr std::string_view name = \"{2}\";\n" 317 | "{11}" 318 | "}};\n", 319 | full_name, 320 | idx, 321 | m.name, 322 | m.result_type, 323 | param_types_str, 324 | m.param_types.size(), 325 | m.is_const ? " const" : "", 326 | param_metas_str, 327 | params_member_str, 328 | m.result_type, 329 | tmpl_params, 330 | ptr_str, 331 | m.is_static ? "" : fmt::format("{}::", full_name) 332 | ); 333 | } 334 | 335 | std::string_view access_to_str(ast::access_kind access){ 336 | switch(access){ 337 | case ast::access_kind::public_: return "public_"; 338 | case ast::access_kind::protected_: return "protected_"; 339 | default: return "private_"; 340 | } 341 | } 342 | 343 | std::string make_class_meta(const ast::class_info &cls){ 344 | std::string 345 | output, 346 | tmpl_param_names, tmpl_params, 347 | attribs_member_str, 348 | bases_member_str, 349 | ctors_member_str, 350 | methods_member_str, 351 | members_member_str; 352 | 353 | for(auto &&tmpl_param : cls.template_params){ 354 | tmpl_param_names += fmt::format(", {}", tmpl_param.name); 355 | if(tmpl_param.is_variadic){ 356 | tmpl_param_names += "..."; 357 | } 358 | 359 | tmpl_params += fmt::format( 360 | ", {}{} {}", 361 | tmpl_param.declarator, (tmpl_param.is_variadic ? "..." : ""), 362 | tmpl_param.name 363 | ); 364 | } 365 | 366 | if(!tmpl_param_names.empty()){ 367 | tmpl_param_names.erase(0, 2); 368 | tmpl_params.erase(0, 2); 369 | } 370 | 371 | std::string full_name = cls.name; 372 | 373 | if(cls.is_specialization){ 374 | std::string spec_args; 375 | for(auto &&arg : cls.template_args){ 376 | spec_args += fmt::format("{}, ", arg); 377 | } 378 | if(!spec_args.empty()){ 379 | spec_args.erase(spec_args.end() - 2); 380 | } 381 | full_name += fmt::format("<{}>", spec_args); 382 | } 383 | else if(!tmpl_param_names.empty()){ 384 | full_name += fmt::format("<{}>", tmpl_param_names); 385 | } 386 | 387 | std::size_t base_idx = 0; 388 | 389 | for(auto &&base : cls.bases){ 390 | std::string type_alias; 391 | 392 | if(base.is_variadic){ 393 | type_alias = fmt::format("metapp::types<{}...>", base.name); 394 | } 395 | else{ 396 | type_alias = base.name; 397 | } 398 | 399 | output += fmt::format( 400 | "template<{0}> struct metapp::detail::class_base_info_data<{1}, {2}>{{\n" 401 | "\t" "static constexpr auto access = metapp::access_kind::{3};\n" 402 | "\t" "static constexpr bool is_variadic = {5};\n" 403 | "\t" "using type = {4};\n" 404 | "}};\n" 405 | "\n", 406 | tmpl_params, 407 | full_name, 408 | base_idx, 409 | access_to_str(base.access), 410 | type_alias, 411 | (base.is_variadic ? "true" : "false") 412 | ); 413 | 414 | bases_member_str += fmt::format( 415 | ",\n" 416 | "\t\t" "metapp::class_base_info<{0}, metapp::value<{1}>>", 417 | full_name, 418 | base_idx++ 419 | ); 420 | } 421 | 422 | if(!bases_member_str.empty()){ 423 | bases_member_str.erase(0, 1); 424 | bases_member_str += "\n\t"; 425 | } 426 | 427 | std::size_t ctor_idx = 0; 428 | 429 | for(auto &&ctor : cls.ctors){ 430 | output += make_ctor_meta(tmpl_params, full_name, *ctor, ctor_idx); 431 | 432 | ctors_member_str += fmt::format( 433 | ",\n" 434 | "\t" "\t" "metapp::class_ctor_info<{0}, metapp::value<{1}>>", 435 | full_name, 436 | ctor_idx++ 437 | ); 438 | } 439 | 440 | std::size_t attrib_idx = 0, method_idx = 0, member_idx = 0; 441 | 442 | for(auto &&attrib : cls.attributes){ 443 | attribs_member_str += fmt::format( 444 | ",\n" 445 | "\t\t" "metapp::attrib_info<{0}, metapp::value<{1}>>", 446 | full_name, 447 | attrib_idx 448 | ); 449 | 450 | std::string args_member_str; 451 | 452 | std::size_t attrib_arg_idx = 0; 453 | 454 | for(auto &&arg : attrib.args()){ 455 | args_member_str += fmt::format( 456 | ",\n" 457 | "\t\t" "metapp::attrib_arg_info<{0}, metapp::value<{1}>, metapp::value<{2}>>", 458 | full_name, 459 | attrib_idx, 460 | attrib_arg_idx 461 | ); 462 | 463 | output += fmt::format( 464 | "template<{4}> struct metapp::detail::attrib_arg_info_data<{0}, {1}, {2}>{{\n" 465 | "\t" "static constexpr std::string_view value = R\"({3})\";\n" 466 | "}};\n" 467 | "\n", 468 | full_name, 469 | attrib_idx, 470 | attrib_arg_idx++, 471 | arg, 472 | tmpl_params 473 | ); 474 | } 475 | 476 | if(!args_member_str.empty()){ 477 | args_member_str.erase(0, 1); 478 | args_member_str += "\n\t"; 479 | } 480 | 481 | output += fmt::format( 482 | "template<{5}> struct metapp::detail::attrib_info_data<{0}, {1}>{{\n" 483 | "\t" "static constexpr std::string_view scope = \"{2}\";\n" 484 | "\t" "static constexpr std::string_view name = \"{3}\";\n" 485 | "\t" "using args = metapp::types<{4}>;\n" 486 | "}};\n" 487 | "\n", 488 | full_name, 489 | attrib_idx++, 490 | attrib.scope(), 491 | attrib.name(), 492 | args_member_str, 493 | tmpl_params 494 | ); 495 | } 496 | 497 | for(auto &&methods : cls.methods){ 498 | for(auto &&m : methods.second){ 499 | methods_member_str += fmt::format( 500 | ",\n" 501 | "\t\t" "metapp::class_method_info<{0}, metapp::value<{1}>>", 502 | full_name, method_idx 503 | ); 504 | 505 | output += make_method_meta(tmpl_params, full_name, *m, method_idx++); 506 | output += "\n"; 507 | } 508 | } 509 | 510 | for(auto &&member : cls.members){ 511 | members_member_str += fmt::format( 512 | ",\n" 513 | "\t\t" "metapp::class_member_info<{0}, metapp::value<{1}>>", 514 | full_name, member_idx 515 | ); 516 | 517 | output += make_member_meta(tmpl_params, full_name, member, member_idx++); 518 | output += "\n"; 519 | } 520 | 521 | if(!attribs_member_str.empty()){ 522 | attribs_member_str.erase(0, 1); 523 | attribs_member_str += "\n\t"; 524 | } 525 | 526 | if(!methods_member_str.empty()){ 527 | methods_member_str.erase(0, 1); 528 | methods_member_str += "\n\t"; 529 | } 530 | 531 | if(!members_member_str.empty()){ 532 | members_member_str.erase(0, 1); 533 | members_member_str += "\n\t"; 534 | } 535 | 536 | if(!ctors_member_str.empty()){ 537 | ctors_member_str.erase(0, 1); 538 | ctors_member_str += "\n\t"; 539 | } 540 | 541 | return fmt::format( 542 | "{0}" 543 | "template<{6}> struct metapp::detail::class_info_data<{1}>{{\n" 544 | "\t" "static constexpr std::string_view name = metapp::type_name<{1}>;\n" 545 | "\t" "using attributes = metapp::types<{5}>;\n" 546 | "\t" "using bases = metapp::types<{7}>;\n" 547 | "\t" "using ctors = metapp::types<{3}>;\n" 548 | "\t" "using methods = metapp::types<{2}>;\n" 549 | "\t" "using members = metapp::types<{4}>;\n" 550 | "}};\n", 551 | 552 | output, 553 | full_name, 554 | methods_member_str, 555 | ctors_member_str, 556 | members_member_str, 557 | 558 | attribs_member_str, // 5 559 | tmpl_params, 560 | bases_member_str 561 | ); 562 | } 563 | 564 | std::string make_enum_meta(const ast::enum_info &enm){ 565 | std::string output; 566 | 567 | std::string values_member_str; 568 | 569 | std::size_t value_idx = 0; 570 | 571 | for(auto &&value : enm.values){ 572 | output += fmt::format( 573 | "template<> struct metapp::detail::enum_value_info_data<{0}, {1}>{{\n" 574 | "\t" "static constexpr std::string_view name = \"{2}\";\n" 575 | "\t" "static constexpr std::uint64_t value = {3};\n" 576 | "}};\n" 577 | "\n", 578 | enm.name, value_idx, value.name, value.value 579 | ); 580 | 581 | values_member_str += fmt::format( 582 | ",\n" 583 | "\t\t" "metapp::enum_value_info<{0}, metapp::value<{1}>>", 584 | enm.name, value_idx++ 585 | ); 586 | } 587 | 588 | if(!values_member_str.empty()){ 589 | values_member_str.erase(0, 1); 590 | values_member_str += "\n\t"; 591 | } 592 | 593 | return fmt::format( 594 | "{0}" 595 | "template<> struct metapp::detail::enum_info_data<{1}>{{\n" 596 | "\t" "using values = metapp::types<{3}>;\n" 597 | "\t" "static constexpr std::string_view name = metapp::type_name<{1}>;\n" 598 | "\t" "static constexpr bool is_scoped = {2};\n" 599 | "}};\n", 600 | output, 601 | enm.name, 602 | enm.is_scoped ? "true" : "false", 603 | values_member_str 604 | ); 605 | } 606 | 607 | std::string make_namespace_meta(const ast::namespace_info &ns){ 608 | std::string output; 609 | 610 | for(auto &&fns : ns.functions){ 611 | for(auto &&fn : fns.second){ 612 | output += make_function_meta(*fn); 613 | output += "\n"; 614 | } 615 | } 616 | 617 | for(auto &&cls : ns.classes){ 618 | output += make_class_meta(*cls.second); 619 | output += "\n"; 620 | } 621 | 622 | for(auto &&enm : ns.enums){ 623 | output += make_enum_meta(*enm.second); 624 | output += "\n"; 625 | } 626 | 627 | for(auto &&inner : ns.namespaces){ 628 | output += make_namespace_meta(*inner.second); 629 | } 630 | 631 | return output; 632 | } 633 | 634 | #endif // MAKE_META_HPP 635 | -------------------------------------------------------------------------------- /ast/parse.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Meta C++ Tool and Library 3 | * Copyright (C) 2022 Keith Hammond 4 | * 5 | * This Source Code Form is subject to the terms of the Mozilla Public 6 | * License, v. 2.0. If a copy of the MPL was not distributed with this 7 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "fmt/format.h" 17 | 18 | #include "metacpp/ast.hpp" 19 | #include "metacpp/meta.hpp" 20 | 21 | #include "clang.hpp" 22 | 23 | namespace fs = std::filesystem; 24 | 25 | namespace astpp::detail{ 26 | template 27 | void print_parse_error(const fs::path &path, Str &&fmt_str, Args &&... fmt_args){ 28 | auto msg = fmt::vformat(std::forward(fmt_str), fmt::make_format_args(std::forward(fmt_args)...)); 29 | fmt::print(stderr, "[ERROR] {}: {}\n", path.string(), msg); 30 | } 31 | 32 | template 33 | void print_parse_warning(const fs::path &path, Str &&fmt_str, Args &&... fmt_args){ 34 | auto msg = fmt::vformat(std::forward(fmt_str), fmt::make_format_args(std::forward(fmt_args)...)); 35 | fmt::print(stderr, "[WARNING] {}: {}\n", path.string(), msg); 36 | } 37 | 38 | template 39 | void print_parse_info(const fs::path &path, Str &&fmt_str, Args &&... fmt_args){ 40 | auto msg = fmt::vformat(std::forward(fmt_str), fmt::make_format_args(std::forward(fmt_args)...)); 41 | fmt::print(stdout, "[INFO] {}: {}\n", path.string(), msg); 42 | } 43 | 44 | template 45 | std::decay_t *store_info(info_map &info, T &&ent){ 46 | auto ptr = std::make_unique>(std::move(std::forward(ent))); 47 | auto ret = ptr.get(); 48 | info.storage.emplace_back(std::move(ptr)); 49 | return ret; 50 | } 51 | 52 | std::optional try_parse(const fs::path &path, info_map &infos, clang::cursor c, namespace_info *ns); 53 | 54 | // TODO: optimize these trim functions 55 | 56 | std::string ltrim(std::string str){ 57 | while(!str.empty() && str.front() == ' '){ 58 | str.erase(str.begin()); 59 | } 60 | 61 | return str; 62 | } 63 | 64 | std::string rtrim(std::string str){ 65 | while(!str.empty() && str.back() == ' '){ 66 | str.erase(str.rbegin().base()); 67 | } 68 | 69 | return str; 70 | } 71 | 72 | std::string trim(std::string str){ 73 | return rtrim(ltrim(std::move(str))); 74 | } 75 | 76 | std::vector parse_attrib_args(const fs::path &path, clang::token_iterator &it, clang::token_iterator end){ 77 | std::vector ret; 78 | 79 | int depth = 1; 80 | std::string arg_str; 81 | while(it != end){ 82 | auto tok_str = it->str(); 83 | 84 | if(tok_str == "("){ 85 | arg_str += tok_str; 86 | 87 | ++depth; 88 | } 89 | else if(tok_str == ")"){ 90 | --depth; 91 | if(depth == 0){ 92 | if(!arg_str.empty()){ 93 | ret.emplace_back(trim(std::move(arg_str))); 94 | } 95 | ++it; 96 | break; 97 | } 98 | else{ 99 | arg_str += tok_str; 100 | } 101 | } 102 | else if(tok_str == "," && depth == 1){ 103 | ret.emplace_back(trim(std::move(arg_str))); 104 | arg_str = ""; 105 | } 106 | else{ 107 | arg_str += " "; 108 | arg_str += tok_str; 109 | } 110 | 111 | ++it; 112 | } 113 | 114 | if(depth != 0){ 115 | print_parse_error(path, "could not find end of attribute argument list"); 116 | } 117 | 118 | return ret; 119 | } 120 | 121 | std::optional parse_attrib(const fs::path &path, clang::token_iterator &it, clang::token_iterator end){ 122 | if(it->kind() != CXToken_Identifier){ 123 | print_parse_error(path, "bad attribute, expected [scope::]name"); 124 | return std::nullopt; 125 | } 126 | 127 | std::string name = it->str(); 128 | 129 | ++it; 130 | 131 | if(it == end){ 132 | return std::make_optional(std::move(name)); 133 | } 134 | 135 | std::string scope = it->str(); 136 | 137 | if(scope == "::"){ 138 | // first string was scope 139 | 140 | ++it; 141 | 142 | if(it->kind() != CXToken_Identifier){ 143 | print_parse_error(path, "bad attribute, expected [scope::]name"); 144 | return std::nullopt; 145 | } 146 | 147 | scope = std::move(name); 148 | name = it->str(); 149 | 150 | ++it; 151 | } 152 | else{ 153 | scope = ""; 154 | } 155 | 156 | std::vector args; 157 | 158 | if(it != end){ 159 | if(it->str() == "("){ 160 | args = parse_attrib_args(path, ++it, end); 161 | } 162 | 163 | if(it != end){ 164 | if(it->str() == ","){ 165 | ++it; 166 | } 167 | else{ 168 | print_parse_warning(path, "bad attribute, ignoring all tokens after '{}'", name); 169 | it = end; 170 | } 171 | } 172 | } 173 | 174 | return std::make_optional(std::move(scope), std::move(name), std::move(args)); 175 | } 176 | 177 | std::vector parse_attribs(const fs::path &path, info_map &infos, clang::token_iterator begin, clang::token_iterator end){ 178 | if(begin->str() != "["){ 179 | return {}; 180 | } 181 | 182 | ++begin; 183 | 184 | if(begin->str() != "["){ 185 | return {}; 186 | } 187 | 188 | ++begin; 189 | 190 | clang::token_iterator new_end_it; 191 | 192 | for(auto it = begin; it != end; ++it){ 193 | if(it->str() == "]"){ 194 | if(new_end_it.is_valid()){ 195 | break; 196 | } 197 | else{ 198 | new_end_it = it; 199 | } 200 | } 201 | else{ 202 | new_end_it = clang::token_iterator{}; 203 | } 204 | } 205 | 206 | if(!new_end_it.is_valid()){ 207 | print_parse_error(path, "could not find end of attribute list"); 208 | return {}; 209 | } 210 | 211 | end = new_end_it; 212 | 213 | std::vector attribs; 214 | 215 | auto it = begin; 216 | 217 | while(it != end){ 218 | auto attr = parse_attrib(path, it, end); 219 | if(attr){ 220 | attribs.emplace_back(std::move(*attr)); 221 | } 222 | } 223 | 224 | return attribs; 225 | } 226 | 227 | std::vector parse_class_attribs(const fs::path &path, info_map &infos, clang::cursor decl){ 228 | const auto toks = decl.tokens(); 229 | 230 | auto toks_begin = toks.begin(); 231 | ++toks_begin; // skip class/struct 232 | 233 | return parse_attribs(path, infos, toks_begin, toks.end()); 234 | } 235 | 236 | std::vector parse_member_attribs(const fs::path &path, info_map &infos, clang::cursor mem){ 237 | const auto toks = mem.tokens(); 238 | return parse_attribs(path, infos, toks.begin(), toks.end()); 239 | } 240 | 241 | std::optional parse_class_ctor(const fs::path &path, info_map &infos, clang::cursor c, class_info *cls){ 242 | if(c.kind() != CXCursor_Constructor){ 243 | return std::nullopt; 244 | } 245 | 246 | auto access = clang_getCXXAccessSpecifier(c); 247 | if(access != CX_CXXPublic){ 248 | return std::nullopt; 249 | } 250 | 251 | class_constructor_info ret; 252 | 253 | ret.ns = cls->ns; 254 | 255 | if(clang_CXXConstructor_isMoveConstructor(c)){ 256 | ret.constructor_kind = constructor_kind::move; 257 | } 258 | else if(clang_CXXConstructor_isCopyConstructor(c)){ 259 | ret.constructor_kind = constructor_kind::copy; 260 | } 261 | else if(clang_CXXConstructor_isDefaultConstructor(c)){ 262 | ret.constructor_kind = constructor_kind::default_; 263 | } 264 | else if(clang_CXXConstructor_isConvertingConstructor(c)){ 265 | ret.constructor_kind = constructor_kind::converting; 266 | } 267 | else{ 268 | ret.constructor_kind = constructor_kind::generic; 269 | } 270 | 271 | auto availability = clang_getCursorAvailability(c); 272 | ret.is_accessable = availability == CXAvailability_Available || availability == CXAvailability_Deprecated; 273 | 274 | std::function replace_self_refs = [](std::string_view type_str){ 275 | return std::string(type_str); 276 | }; 277 | 278 | if(!cls->template_params.empty()){ 279 | const std::string cls_name = cls->name.substr(2); // after global namespace :: 280 | const std::string cls_name_opened = fmt::format("{}<", cls_name); 281 | 282 | std::string full_name = "::" + cls_name_opened; 283 | 284 | for(auto &&template_param : cls->template_params){ 285 | full_name += fmt::format("{}, ", template_param.name); 286 | } 287 | 288 | full_name.erase(full_name.size() - 2); 289 | full_name.insert(full_name.end(), '>'); 290 | 291 | replace_self_refs = [cls_name, cls_name_opened, full_name](std::string_view type_str){ 292 | auto ret = std::string(type_str); 293 | std::size_t name_pos = 0; 294 | 295 | while(1){ 296 | name_pos = ret.find(cls_name, name_pos); 297 | if(name_pos == std::string::npos) break; 298 | 299 | auto opened_pos = ret.find(cls_name_opened); 300 | if(opened_pos != name_pos){ 301 | ret.replace(name_pos, cls_name.size(), full_name); 302 | name_pos += full_name.size(); 303 | } 304 | else{ 305 | name_pos += cls_name_opened.size(); 306 | } 307 | } 308 | 309 | return ret; 310 | }; 311 | } 312 | 313 | int num_params = clang_Cursor_getNumArguments(c); 314 | 315 | if(num_params > 0){ 316 | ret.param_names.reserve(num_params); 317 | ret.param_types.reserve(num_params); 318 | 319 | for(int i = 0; i < num_params; i++){ 320 | clang::cursor arg_cursor = clang_Cursor_getArgument(c, i); 321 | clang::type arg_type = clang_getCursorType(arg_cursor); 322 | 323 | auto arg_type_name = arg_type.spelling(); 324 | 325 | ret.param_names.emplace_back(arg_cursor.spelling()); 326 | ret.param_types.emplace_back(replace_self_refs(arg_type_name)); 327 | } 328 | } 329 | 330 | return ret; 331 | } 332 | 333 | std::optional parse_class_dtor(const fs::path &path, info_map &infos, clang::cursor c, namespace_info *ns){ 334 | if(c.kind() != CXCursor_Destructor){ 335 | return std::nullopt; 336 | } 337 | 338 | class_destructor_info ret; 339 | 340 | ret.ns = ns; 341 | 342 | return ret; 343 | } 344 | 345 | std::string resolve_namespaces(clang::cursor c){ 346 | std::string ret; 347 | 348 | while(1){ 349 | clang::cursor parent = clang_getCursorSemanticParent(c); 350 | if(clang_isInvalid(parent.kind()) || clang_isTranslationUnit(parent.kind())){ 351 | break; 352 | } 353 | else if(parent.kind() == CXCursor_ClassDecl || parent.kind() == CXCursor_Namespace){ 354 | auto ns_str = fmt::format("::{}", parent.spelling()); 355 | ret.insert(ret.begin(), ns_str.begin(), ns_str.end()); 356 | } 357 | 358 | c = parent; 359 | } 360 | 361 | return ret; 362 | } 363 | 364 | std::string resolve_typename(clang::type t){ 365 | clang::type canon = clang_getCanonicalType(t); 366 | if(canon.is_valid()){ 367 | t = canon; 368 | } 369 | 370 | std::string ret = t.spelling(); 371 | 372 | clang::cursor decl = clang_getTypeDeclaration(t); 373 | if(!decl.is_valid()){ 374 | return ret; 375 | } 376 | 377 | if(decl.kind() == CXCursor_ClassTemplate || decl.kind() == CXCursor_ClassTemplatePartialSpecialization){ 378 | ret = fmt::format("{}::{}", resolve_namespaces(decl), decl.spelling()); 379 | 380 | auto num_tmpl_args = clang_Type_getNumTemplateArguments(t); 381 | 382 | if(num_tmpl_args > 0){ 383 | ret += "<"; 384 | 385 | for(int i = 0; i < num_tmpl_args; i++){ 386 | clang::type arg_type = clang_Type_getTemplateArgumentAsType(t, i); 387 | ret += fmt::format("{}, ", resolve_typename(arg_type)); 388 | } 389 | 390 | ret.erase(ret.size() - 2); 391 | ret += ">"; 392 | } 393 | } 394 | 395 | return ret; 396 | } 397 | 398 | std::optional parse_class_member(const fs::path &path, info_map &infos, clang::cursor c, class_info *cls){ 399 | if(c.kind() != CXCursor_FieldDecl){ 400 | return std::nullopt; 401 | } 402 | 403 | auto access = clang_getCXXAccessSpecifier(c); 404 | if(access != CX_CXXPublic){ 405 | return std::nullopt; 406 | } 407 | 408 | class_member_info ret; 409 | 410 | auto member_type = c.type(); 411 | auto member_type_str = resolve_typename(member_type); 412 | 413 | ret.type = std::move(member_type_str); 414 | ret.ns = cls->ns; 415 | ret.name = c.spelling(); 416 | ret.attributes = parse_member_attribs(path, infos, c); 417 | 418 | std::string decl_str; 419 | 420 | const auto toks = c.tokens(); 421 | 422 | for(auto &&tok : toks){ 423 | decl_str += " " + tok.str(); 424 | } 425 | 426 | auto availability = clang_getCursorAvailability(c); 427 | ret.is_accessable = availability == CXAvailability_Available || availability == CXAvailability_Deprecated; 428 | 429 | return ret; 430 | } 431 | 432 | std::optional parse_class_method(const fs::path &path, info_map &infos, clang::cursor c, class_info *cls){ 433 | if(c.kind() != CXCursor_CXXMethod){ 434 | return std::nullopt; 435 | } 436 | 437 | auto access = clang_getCXXAccessSpecifier(c); 438 | if(access != CX_CXXPublic){ 439 | return std::nullopt; 440 | } 441 | 442 | class_method_info ret; 443 | 444 | ret.ns = cls->ns; 445 | ret.name = c.spelling(); 446 | ret.is_static = clang_CXXMethod_isStatic(c); 447 | ret.is_const = clang_CXXMethod_isConst(c); 448 | ret.is_virtual = clang_CXXMethod_isVirtual(c); 449 | ret.is_pure_virtual = clang_CXXMethod_isPureVirtual(c); 450 | ret.is_defaulted = clang_CXXMethod_isDefaulted(c); 451 | 452 | auto availability = clang_getCursorAvailability(c); 453 | ret.is_accessable = availability == CXAvailability_Available || availability == CXAvailability_Deprecated; 454 | 455 | std::function replace_self_refs = [](std::string_view type_str){ 456 | return std::string(type_str); 457 | }; 458 | 459 | if(!cls->template_params.empty()){ 460 | const std::string cls_name = cls->name.substr(2); // after global namespace :: 461 | const std::string cls_name_opened = fmt::format("{}<", cls_name); 462 | 463 | std::string full_name = "::" + cls_name_opened; 464 | 465 | for(auto &&template_param : cls->template_params){ 466 | if(template_param.is_variadic){ 467 | full_name += fmt::format("{}..., ", template_param.name); 468 | } 469 | else{ 470 | full_name += fmt::format("{}, ", template_param.name); 471 | } 472 | } 473 | 474 | full_name.erase(full_name.size() - 2); 475 | full_name.insert(full_name.end(), '>'); 476 | 477 | replace_self_refs = [cls_name, cls_name_opened, full_name](std::string_view type_str){ 478 | auto ret = std::string(type_str); 479 | std::size_t name_pos = 0; 480 | 481 | while(1){ 482 | name_pos = ret.find(cls_name, name_pos); 483 | if(name_pos == std::string::npos) break; 484 | 485 | auto opened_pos = ret.find(cls_name_opened); 486 | if(opened_pos != name_pos){ 487 | ret.replace(name_pos, cls_name.size(), full_name); 488 | name_pos += full_name.size(); 489 | } 490 | else{ 491 | name_pos += cls_name_opened.size(); 492 | } 493 | } 494 | 495 | return ret; 496 | }; 497 | } 498 | 499 | ret.name = replace_self_refs(ret.name); 500 | 501 | auto fn_type = c.type(); 502 | 503 | ret.is_noexcept = 504 | clang_getExceptionSpecificationType(fn_type) == CXCursor_ExceptionSpecificationKind_BasicNoexcept; 505 | 506 | auto fn_type_str = fn_type.spelling(); 507 | 508 | { 509 | auto result_type = clang::type(clang_getResultType(fn_type)); 510 | auto result_type_str = result_type.spelling(); 511 | 512 | if(result_type.kind() == CXType_Typedef){ 513 | result_type_str = fmt::format("typename {}", result_type_str); 514 | } 515 | 516 | ret.result_type = replace_self_refs(result_type_str); 517 | } 518 | 519 | const int num_params = clang_Cursor_getNumArguments(c); 520 | 521 | if(num_params > 0){ 522 | ret.param_names.reserve(num_params); 523 | ret.param_types.reserve(num_params); 524 | 525 | for(int i = 0; i < num_params; i++){ 526 | clang::cursor param_cursor = clang_Cursor_getArgument(c, i); 527 | 528 | auto param_name = param_cursor.spelling(); 529 | auto param_type = param_cursor.type(); 530 | auto param_type_str = param_type.spelling(); 531 | 532 | if(param_type.kind() == CXType_Typedef){ 533 | param_type_str = fmt::format("typename {}", param_type_str); 534 | } 535 | 536 | ret.param_names.emplace_back(std::move(param_name)); 537 | ret.param_types.emplace_back(replace_self_refs(param_type_str)); 538 | } 539 | } 540 | 541 | return ret; 542 | } 543 | 544 | std::optional parse_class_decl(const fs::path &path, info_map &infos, clang::cursor c, namespace_info *ns); 545 | 546 | std::optional parse_class_base(const fs::path &path, info_map &infos, clang::cursor c, class_info *cls){ 547 | if(c.kind() != CXCursor_CXXBaseSpecifier){ 548 | return std::nullopt; 549 | } 550 | 551 | std::string base_type_str; 552 | 553 | bool is_template_alias = true; 554 | 555 | clang::type base_type = c.type(); 556 | 557 | clang::cursor base_decl = clang_getTypeDeclaration(base_type); 558 | if(!clang_isInvalid(base_decl.kind())){ 559 | 560 | if(base_decl.kind() == CXCursor_TypeAliasDecl || base_decl.kind() == CXCursor_TypeAliasTemplateDecl){ 561 | is_template_alias = false; 562 | base_type_str = c.spelling(); 563 | } 564 | else{ 565 | std::string resolved_name = fmt::format("{}::{}", resolve_namespaces(base_decl), base_decl.spelling()); 566 | 567 | base_type_str = std::move(resolved_name); 568 | } 569 | } 570 | else{ 571 | base_type_str = base_type.spelling(); 572 | } 573 | 574 | class_base_info base; 575 | base.name = std::move(base_type_str); 576 | 577 | /* 578 | auto toks = c.tokens(); 579 | auto tok_it = toks.begin(); 580 | auto tok_end = toks.end(); 581 | 582 | while( 583 | tok_it != tok_end && ( 584 | tok_it->str() == "public" || 585 | tok_it->str() == "protected" || 586 | tok_it->str() == "private" || 587 | tok_it->str() == "virtual" 588 | ) 589 | ){ 590 | ++tok_it; 591 | } 592 | 593 | std::string base_str; 594 | for(auto it = tok_it; it != tok_end; ++it){ 595 | base_str += it->str(); 596 | } 597 | */ 598 | 599 | std::unordered_map param_map; 600 | for(auto &¶m : cls->template_params){ 601 | param_map[param.name] = ¶m; 602 | } 603 | 604 | const auto num_tmpl_args = clang_Type_getNumTemplateArguments(base_type); 605 | 606 | if(is_template_alias && num_tmpl_args != -1){ 607 | std::string template_args_str; 608 | 609 | for(int i = 0; i < num_tmpl_args; i++){ 610 | clang::type arg_type = clang_Type_getTemplateArgumentAsType(base_type, i); 611 | const auto arg_type_name = arg_type.spelling(); 612 | template_args_str += fmt::format("{}, ", arg_type_name); 613 | 614 | auto res = param_map.find(arg_type_name); 615 | if(res != param_map.end()){ 616 | if(res->second->is_variadic){ 617 | base.is_variadic = true; 618 | } 619 | } 620 | } 621 | 622 | if(!template_args_str.empty()){ 623 | template_args_str.erase(template_args_str.size() - 2); 624 | } 625 | 626 | base.name += fmt::format("<{}>", template_args_str); 627 | } 628 | 629 | switch(clang_getCXXAccessSpecifier(c)){ 630 | case CX_CXXPublic:{ 631 | base.access = access_kind::public_; 632 | break; 633 | } 634 | 635 | case CX_CXXProtected:{ 636 | base.access = access_kind::protected_; 637 | break; 638 | } 639 | 640 | default:{ 641 | base.access = access_kind::private_; 642 | break; 643 | } 644 | } 645 | 646 | clang::cursor base_decl_c = clang_getTypeDeclaration(base_type); 647 | 648 | if(base_decl_c.is_null()){ 649 | print_parse_error(path, "Failed to get base class declaration for '{}'", c.spelling()); 650 | } 651 | else if(base_decl_c.kind() == CXCursor_ClassTemplate){ 652 | auto base_decl_opt = detail::parse_class_decl(path, infos, base_decl_c, &infos.global); 653 | } 654 | 655 | return base; 656 | } 657 | 658 | std::optional parse_class_decl( 659 | const fs::path &path, info_map &infos, 660 | clang::cursor c, namespace_info *ns 661 | ){ 662 | const bool is_template = 663 | c.kind() == CXCursor_ClassTemplate || 664 | c.kind() == CXCursor_ClassTemplatePartialSpecialization; 665 | 666 | if(!is_template && c.kind() != CXCursor_ClassDecl){ 667 | return std::nullopt; 668 | } 669 | 670 | auto access = clang_getCXXAccessSpecifier(c); 671 | if(access != CX_CXXInvalidAccessSpecifier && access != CX_CXXPublic){ 672 | return std::nullopt; 673 | } 674 | 675 | auto class_name = c.spelling(); 676 | auto class_type = c.type(); 677 | auto class_type_str = class_type.spelling(); 678 | 679 | class_info ret; 680 | 681 | { 682 | auto toks = c.tokens(); 683 | auto toks_begin = toks.begin(); 684 | 685 | ++toks_begin; // skip class/struct keyword 686 | 687 | ret.attributes = parse_attribs(path, infos, toks_begin, toks.end()); 688 | } 689 | 690 | ret.ns = ns; 691 | ret.name = ns->name + "::" + class_name; 692 | ret.is_abstract = clang_CXXRecord_isAbstract(c); 693 | ret.is_template = is_template; 694 | ret.is_specialization = c.kind() == CXCursor_ClassTemplatePartialSpecialization; 695 | 696 | std::size_t ctor_idx = 0, member_idx = 0, method_idx = 0; 697 | 698 | bool in_template = is_template; 699 | 700 | c.visit_children([&](clang::cursor inner, clang::cursor){ 701 | if(in_template){ 702 | switch(inner.kind()){ 703 | case CXCursor_TemplateTypeParameter:{ 704 | template_param_info param; 705 | 706 | auto inner_toks = inner.tokens(); 707 | 708 | auto toks_begin = inner_toks.begin(); 709 | auto toks_end = inner_toks.end(); 710 | 711 | auto toks_it = toks_begin; 712 | 713 | param.declarator = toks_it->str(); 714 | 715 | ++toks_it; 716 | 717 | if(toks_it != toks_end){ 718 | param.is_variadic = toks_it->str() == "..."; 719 | } 720 | else{ 721 | param.is_variadic = false; 722 | } 723 | 724 | param.name = inner.spelling(); 725 | 726 | ret.template_params.emplace_back(std::move(param)); 727 | 728 | return; 729 | } 730 | 731 | case CXCursor_TemplateTemplateParameter:{ 732 | template_param_info param; 733 | 734 | print_parse_error(path, "template template parameter '{}' for '{}'; not currently supported", inner.spelling(), ret.name); 735 | 736 | break; 737 | } 738 | 739 | default:{ 740 | in_template = false; 741 | break; 742 | } 743 | } 744 | } 745 | 746 | if(auto base_opt = parse_class_base(path, infos, inner, &ret); base_opt){ 747 | ret.bases.emplace_back(std::move(*base_opt)); 748 | } 749 | else if(auto class_decl = parse_class_decl(path, infos, inner, ns); class_decl){ 750 | auto ptr = store_info(infos, std::move(*class_decl)); 751 | ret.classes[ptr->name] = ptr; 752 | } 753 | else if(auto ctor = parse_class_ctor(path, infos, inner, &ret); ctor){ 754 | auto ptr = store_info(infos, std::move(*ctor)); 755 | ret.ctors.emplace_back(ptr); 756 | } 757 | else if(auto dtor = parse_class_dtor(path, infos, inner, ns); dtor){ 758 | auto ptr = store_info(infos, std::move(*dtor)); 759 | ret.dtor = ptr; 760 | } 761 | else if(auto method = parse_class_method(path, infos, inner, &ret); method){ 762 | auto ptr = store_info(infos, std::move(*method)); 763 | ret.methods[ptr->name].emplace_back(ptr); 764 | } 765 | else if(auto member = parse_class_member(path, infos, inner, &ret); member){ 766 | ret.members.emplace_back(std::move(*member)); 767 | } 768 | 769 | // TODO: handle access kinds 770 | 771 | // skip anything else 772 | }); 773 | 774 | if(ret.is_specialization){ 775 | auto num_spec_params = clang_Type_getNumTemplateArguments(class_type); 776 | for(int i = 0; i < num_spec_params; i++){ 777 | clang::type spec_param_type = clang_Type_getTemplateArgumentAsType(class_type, i); 778 | 779 | std::string spec_param_str = spec_param_type.spelling(); 780 | 781 | clang::cursor spec_param_decl = clang_getTypeDeclaration(spec_param_type); 782 | if(spec_param_decl.is_valid()){ 783 | const auto namespaces = resolve_namespaces(spec_param_decl); 784 | if(!namespaces.empty()){ 785 | spec_param_str = fmt::format("{}::{}", namespaces, spec_param_str); 786 | } 787 | } 788 | 789 | ret.template_args.emplace_back(std::move(spec_param_str)); 790 | } 791 | 792 | std::size_t tmpl_param_idx = 0; 793 | for(const auto &template_param : ret.template_params){ 794 | const auto param_alias = fmt::format("type-parameter-0-{}", tmpl_param_idx++); 795 | for(std::string &template_arg : ret.template_args){ 796 | auto alias_pos = template_arg.find(param_alias); 797 | while(alias_pos != std::string::npos){ 798 | template_arg.replace(alias_pos, param_alias.size(), template_param.name); 799 | alias_pos = template_arg.find(param_alias); 800 | } 801 | } 802 | } 803 | 804 | std::string specialization_params; 805 | 806 | for(auto &&template_arg : ret.template_args){ 807 | specialization_params += fmt::format("{}, ", template_arg); 808 | } 809 | 810 | if(!specialization_params.empty()){ 811 | specialization_params.erase(specialization_params.size() - 2); 812 | } 813 | } 814 | 815 | return ret; 816 | } 817 | 818 | std::optional parse_enum_decl(const fs::path &path, info_map &infos, clang::cursor c, namespace_info *ns){ 819 | if(c.kind() != CXCursor_EnumDecl){ 820 | return std::nullopt; 821 | } 822 | 823 | enum_info ret; 824 | 825 | ret.name = ns->name + "::" + c.spelling(); 826 | ret.is_scoped = clang_EnumDecl_isScoped(c); 827 | 828 | c.visit_children([&](clang::cursor value_c, clang::cursor){ 829 | if(value_c.kind() != CXCursor_EnumConstantDecl){ 830 | return; 831 | } 832 | 833 | enum_value_info value; 834 | value.name = value_c.spelling(); 835 | value.value = clang_getEnumConstantDeclUnsignedValue(value_c); 836 | ret.values.emplace_back(std::move(value)); 837 | }); 838 | 839 | return ret; 840 | } 841 | 842 | std::optional parse_function_decl(const fs::path &path, info_map &infos, clang::cursor c, namespace_info *ns){ 843 | if(c.kind() != CXCursor_FunctionDecl){ 844 | return std::nullopt; 845 | } 846 | 847 | auto def_cursor = clang_getCursorDefinition(c); 848 | if(!clang_Cursor_isNull(def_cursor)){ 849 | c = def_cursor; 850 | } 851 | 852 | function_info ret; 853 | 854 | ret.name = ns->name + "::" + c.spelling(); 855 | 856 | auto fn_type = c.type(); 857 | auto fn_type_str = resolve_typename(fn_type); 858 | 859 | clang::type result_type = clang_getResultType(fn_type); 860 | ret.result_type = resolve_typename(result_type); 861 | 862 | int num_params = clang_Cursor_getNumArguments(c); 863 | 864 | if(num_params > 0){ 865 | ret.param_names.reserve(num_params); 866 | ret.param_types.reserve(num_params); 867 | 868 | for(int i = 0; i < num_params; i++){ 869 | clang::cursor arg_cursor = clang_Cursor_getArgument(c, i); 870 | auto arg_type = arg_cursor.type(); 871 | 872 | auto arg_name = arg_cursor.spelling(); 873 | auto arg_type_name = resolve_typename(arg_type); 874 | 875 | ret.param_names.emplace_back(std::move(arg_name)); 876 | ret.param_types.emplace_back(std::move(arg_type_name)); 877 | } 878 | } 879 | 880 | return ret; 881 | } 882 | 883 | entity_info *parse_namespace_inner(const fs::path &path, info_map &infos, clang::cursor c, namespace_info *ns){ 884 | auto res = try_parse(path, infos, c, ns); 885 | if(!res) return nullptr; 886 | 887 | entity_info *ret = nullptr; 888 | 889 | std::visit( 890 | meta::overload( 891 | [&](function_info &fn_info){ 892 | auto ptr = store_info(infos, std::move(fn_info)); 893 | ret = ptr; 894 | ns->functions[ptr->name].emplace_back(ptr); 895 | }, 896 | [&](class_info &cls){ 897 | auto ptr = store_info(infos, std::move(cls)); 898 | ret = ptr; 899 | ns->classes[ptr->name] = ptr; 900 | }, 901 | [&](enum_info &enm){ 902 | auto ptr = store_info(infos, std::move(enm)); 903 | ret = ptr; 904 | ns->enums[ptr->name] = ptr; 905 | }, 906 | [&](namespace_info &child_ns){ 907 | auto ptr = store_info(infos, std::move(child_ns)); 908 | ret = ptr; 909 | infos.namespaces[ptr->name] = ptr; 910 | ns->namespaces[ptr->name] = ptr; 911 | }, 912 | [&](type_alias_info &alias){ 913 | auto ptr = store_info(infos, std::move(alias)); 914 | ret = ptr; 915 | ns->aliases[ptr->name] = ptr; 916 | } 917 | ), 918 | *res 919 | ); 920 | 921 | return ret; 922 | } 923 | 924 | std::optional parse_type_alias(const fs::path &path, info_map &infos, clang::cursor c, namespace_info *ns){ 925 | if(c.kind() != CXCursor_TypeAliasDecl && c.kind() != CXCursor_TypedefDecl){ 926 | return std::nullopt; 927 | } 928 | 929 | auto access = clang_getCXXAccessSpecifier(c); 930 | if(access != CX_CXXInvalidAccessSpecifier && access != CX_CXXPublic){ 931 | return std::nullopt; 932 | } 933 | 934 | type_alias_info ret; 935 | 936 | ret.name = ns->name + "::" + c.spelling(); 937 | 938 | auto type = clang_getTypedefDeclUnderlyingType(c); 939 | 940 | ret.aliased = clang::detail::convert_str(clang_getTypeSpelling(type)); 941 | 942 | return ret; 943 | } 944 | 945 | std::optional parse_namespace(const fs::path &path, info_map &infos, clang::cursor c, namespace_info *ns){ 946 | if(c.kind() != CXCursor_Namespace){ 947 | return std::nullopt; 948 | } 949 | 950 | auto inner_name = ns->name + "::" + c.spelling(); 951 | auto inner_res = infos.namespaces.find(inner_name); 952 | 953 | if(inner_res != infos.namespaces.end()){ 954 | auto inner = inner_res->second; 955 | 956 | c.visit_children( 957 | [&](clang::cursor child, clang::cursor){ 958 | parse_namespace_inner(path, infos, child, inner); 959 | } 960 | ); 961 | 962 | return std::nullopt; 963 | } 964 | else{ 965 | namespace_info ret; 966 | 967 | ret.ns = ns; 968 | ret.name = std::move(inner_name); 969 | 970 | c.visit_children( 971 | [&](clang::cursor child, clang::cursor){ 972 | parse_namespace_inner(path, infos, child, &ret); 973 | } 974 | ); 975 | 976 | return ret; 977 | } 978 | } 979 | 980 | std::optional try_parse(const fs::path &path, info_map &infos, clang::cursor c, namespace_info *ns){ 981 | if(!clang_Location_isFromMainFile(clang_getCursorLocation(c))){ 982 | return std::nullopt; 983 | } 984 | else if(auto fn_decl = detail::parse_function_decl(path, infos, c, ns); fn_decl){ 985 | return std::make_optional(std::move(*fn_decl)); 986 | } 987 | else if(auto class_decl = detail::parse_class_decl(path, infos, c, ns); class_decl){ 988 | return std::make_optional(std::move(*class_decl)); 989 | } 990 | else if(auto enum_decl = detail::parse_enum_decl(path, infos, c, ns); enum_decl){ 991 | return std::make_optional(std::move(*enum_decl)); 992 | } 993 | else if(auto fn_decl = detail::parse_function_decl(path, infos, c, ns); fn_decl){ 994 | return std::make_optional(std::move(*fn_decl)); 995 | } 996 | else if(auto ns_decl = detail::parse_namespace(path, infos, c, ns); ns_decl){ 997 | return std::make_optional(std::move(*ns_decl)); 998 | } 999 | else if(auto alias = detail::parse_type_alias(path, infos, c, ns); alias){ 1000 | return std::make_optional(std::move(*alias)); 1001 | } 1002 | 1003 | return std::nullopt; 1004 | } 1005 | } 1006 | 1007 | ast::info_map ast::parse(const fs::path &path, const compile_info &info, std::vector cmd_args, bool verbose){ 1008 | using namespace astpp; 1009 | 1010 | auto path_utf8 = path.u8string(); 1011 | auto abs_path = fs::absolute(path); 1012 | 1013 | if(!fs::exists(path)){ 1014 | auto msg = fmt::format("File '{}' does not exist", path_utf8); 1015 | throw std::runtime_error(msg); 1016 | } 1017 | else if(!fs::is_regular_file(path)){ 1018 | auto msg = fmt::format("'{}' is not a regular file", path_utf8); 1019 | throw std::runtime_error(msg); 1020 | } 1021 | 1022 | auto include_dirs = info.all_include_dirs(); 1023 | 1024 | cmd_args.emplace_back("-DMETACPP_TOOL_RUN"); 1025 | 1026 | while(1){ 1027 | auto res = std::find_if( 1028 | cmd_args.begin(), cmd_args.end(), 1029 | [](auto &&opt){ 1030 | auto opt_prefix = std::string_view(opt).substr(0, 2); 1031 | return 1032 | (opt_prefix[0] == '@') || // @ at the start of response files (just in case) 1033 | (opt == "-flto") || 1034 | (opt == "-Werror") || 1035 | (opt == "-fno-fat-lto-objects") || 1036 | (std::string_view(opt).substr(0, 9) == "--target=") || 1037 | (std::string_view(opt).substr(0, 14) == "--driver-mode=") 1038 | ; 1039 | } 1040 | ); 1041 | if(res == cmd_args.end()) break; 1042 | else cmd_args.erase(res); 1043 | } 1044 | 1045 | cmd_args.emplace_back("-c"); 1046 | cmd_args.emplace_back("-x"); 1047 | cmd_args.emplace_back("c++-header"); 1048 | 1049 | for(auto &&dir : include_dirs){ 1050 | auto dir_utf8 = dir.u8string(); 1051 | cmd_args.emplace_back(fmt::format("-I{}", dir_utf8)); 1052 | } 1053 | 1054 | cmd_args.emplace_back("-Wno-ignored-optimization-argument"); 1055 | 1056 | static clang::index index; 1057 | 1058 | auto retrieve = [&cmd_args](const std::string &p) -> std::pair{ 1059 | static std::unordered_map> tus; 1060 | 1061 | auto res = tus.find(p); 1062 | if(res != tus.end()){ 1063 | return std::make_pair(&res->second.first, &res->second.second); 1064 | } 1065 | 1066 | auto emplace_res = tus.try_emplace( 1067 | p, 1068 | std::piecewise_construct, 1069 | std::forward_as_tuple(), 1070 | std::forward_as_tuple(index, p, cmd_args) 1071 | ); 1072 | 1073 | if(!emplace_res.second){ 1074 | return std::make_pair(nullptr, nullptr); 1075 | } 1076 | 1077 | return std::make_pair(&res->second.first, &res->second.second); 1078 | }; 1079 | 1080 | if(verbose){ 1081 | std::string options_str; 1082 | 1083 | for(auto &&opt : cmd_args){ 1084 | options_str += fmt::format(" {}", opt); 1085 | } 1086 | 1087 | fmt::print( 1088 | "clang invocation for {}:{}\n", 1089 | path_utf8, options_str 1090 | ); 1091 | 1092 | std::fflush(stdout); 1093 | } 1094 | 1095 | clang::translation_unit tu(index, path, cmd_args); 1096 | 1097 | const unsigned int num_diag = clang_getNumDiagnostics(tu); 1098 | 1099 | bool found_err = false; 1100 | 1101 | for(unsigned int i = 0; i < num_diag; i++){ 1102 | CXDiagnostic diagnotic = clang_getDiagnostic(tu, i); 1103 | 1104 | auto err_str = clang::detail::convert_str(clang_formatDiagnostic(diagnotic, clang_defaultDiagnosticDisplayOptions())); 1105 | 1106 | if(err_str.find("error:") != std::string::npos){ 1107 | found_err = true; 1108 | fmt::print(stderr, "{}\n", err_str); 1109 | } 1110 | } 1111 | 1112 | if(found_err){ 1113 | throw std::runtime_error("AST parsing failed with errors"); 1114 | } 1115 | 1116 | info_map ret; 1117 | ret.global.ns = nullptr; 1118 | 1119 | std::function visitor = [&](clang::cursor cursor, clang::cursor parent){ 1120 | if(cursor.kind() == CXCursor_InclusionDirective){ 1121 | auto included_file = clang_getIncludedFile(cursor); 1122 | auto include_str = clang::detail::convert_str(clang_getFileName(included_file)); 1123 | 1124 | //auto included_tu = get_tu(include_str, build_dir); 1125 | return; 1126 | } 1127 | else if(cursor.kind() == CXCursor_UsingDirective){ 1128 | return; // skip using directives 1129 | } 1130 | 1131 | auto entity = ast::detail::parse_namespace_inner(path, ret, cursor, &ret.global); 1132 | if(!entity){ 1133 | //ast::detail::print_parse_warning(path, "Unrecognized cursor '{}' of kind '{}'", cursor.spelling(), cursor.kind_spelling()); 1134 | } 1135 | }; 1136 | 1137 | tu.get_cursor().visit_children(visitor); 1138 | 1139 | return ret; 1140 | } 1141 | 1142 | std::string astpp::compiler_version(){ 1143 | return clang::version(); 1144 | } 1145 | -------------------------------------------------------------------------------- /include/metacpp/refl.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Meta C++ Tool and Library 3 | * Copyright (C) 2022 Keith Hammond 4 | * 5 | * This Source Code Form is subject to the terms of the Mozilla Public 6 | * License, v. 2.0. If a copy of the MPL was not distributed with this 7 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef METACPP_REFL_HPP 11 | #define METACPP_REFL_HPP 1 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "meta.hpp" 21 | 22 | /** 23 | * @defgroup Refl Run-time reflection utilities 24 | * @{ 25 | */ 26 | 27 | namespace reflpp{ 28 | struct args_pack_base; 29 | 30 | namespace detail{ 31 | struct attribute_info_helper{ 32 | virtual std::string_view scope() const noexcept = 0; 33 | virtual std::string_view name() const noexcept = 0; 34 | virtual std::size_t num_args() const noexcept = 0; 35 | virtual std::string_view arg(std::size_t idx) const noexcept = 0; 36 | }; 37 | 38 | struct type_info_helper{ 39 | virtual std::string_view name() const noexcept = 0; 40 | virtual std::size_t size() const noexcept = 0; 41 | virtual std::size_t alignment() const noexcept = 0; 42 | virtual void destroy(void *p) const noexcept = 0; 43 | virtual void *construct(void *p, args_pack_base *args) const = 0; 44 | virtual std::size_t num_attributes() const noexcept{ return 0; } 45 | virtual const attribute_info_helper *attribute(std::size_t idx) const noexcept{ return nullptr; } 46 | virtual std::type_index type_index() const noexcept = 0; 47 | }; 48 | 49 | struct num_info_helper; 50 | struct int_info_helper; 51 | struct function_info_helper; 52 | struct fn_ptr_info_helper; 53 | struct class_info_helper; 54 | struct enum_info_helper; 55 | 56 | struct class_member_helper; 57 | struct class_method_helper; 58 | 59 | struct enum_value_helper; 60 | 61 | template 62 | struct reflect_helper; 63 | }; 64 | 65 | /** 66 | * @brief Handle to information about a type attribute. 67 | */ 68 | using attribute_info = const detail::attribute_info_helper*; 69 | 70 | /** 71 | * @brief Handle to information about a type. 72 | */ 73 | using type_info = const detail::type_info_helper*; 74 | 75 | /** 76 | * @brief Handle to information about an arithmetic type. 77 | */ 78 | using num_info = const detail::num_info_helper*; 79 | 80 | /** 81 | * @brief Handle to information about an integer type. 82 | */ 83 | using int_info = const detail::int_info_helper*; 84 | 85 | /** 86 | * @brief Handle to information about a function. 87 | */ 88 | using function_info = const detail::function_info_helper*; 89 | 90 | /** 91 | * @brief Handle to information about a function pointer. 92 | */ 93 | using fn_ptr_info = const detail::fn_ptr_info_helper*; 94 | 95 | /** 96 | * @brief Handle to information about a class/struct. 97 | */ 98 | using class_info = const detail::class_info_helper*; 99 | 100 | /** 101 | * @brief Handle to information about an enum. 102 | */ 103 | using enum_info = const detail::enum_info_helper*; 104 | 105 | /** 106 | * @brief Handle to information about a class method. 107 | */ 108 | using class_method_info = const detail::class_method_helper*; 109 | 110 | /** 111 | * @brief Handle to information about a class member. 112 | */ 113 | using class_member_info = const detail::class_member_helper*; 114 | 115 | /** 116 | * @brief Try to dynamically get information about a type by name. 117 | * @param name type name to search for 118 | * @returns `nullptr` on error, handle to found type on success 119 | */ 120 | type_info reflect(std::string_view name); 121 | 122 | /** 123 | * @brief Get information about every reflected type in a program. 124 | * @warning this can be an expensive operation 125 | * @returns information about all reflected types 126 | */ 127 | std::vector reflect_all(); 128 | 129 | /** 130 | * @brief Get information about every reflected class type in a program. 131 | * @warning this can be an expensive operation 132 | * @returns information about all reflected classes 133 | */ 134 | std::vector reflect_all_classes(); 135 | 136 | /** 137 | * @brief Try to dynamically get information about a class type. 138 | * @see reflect 139 | */ 140 | class_info reflect_class(std::string_view name); 141 | 142 | /** 143 | * @brief Try to dynamically get information about an enum type. 144 | * @see reflect 145 | */ 146 | enum_info reflect_enum(std::string_view name); 147 | 148 | /** 149 | * @brief Helper function for reflecting class types. 150 | * @see reflect_class 151 | */ 152 | inline class_info class_(std::string_view name){ return reflect_class(name); } 153 | 154 | /** 155 | * @brief Helper function for reflecting enum types. 156 | * @see reflect_enum 157 | */ 158 | inline enum_info enum_(std::string_view name){ return reflect_enum(name); } 159 | 160 | /** 161 | * @brief Get information about a function from it's pointer. 162 | * @warning Currently unimplemented. 163 | */ 164 | template 165 | function_info reflect(Ret(*ptr)(Args...)){ 166 | return nullptr; 167 | } 168 | 169 | /** 170 | * @brief Get an attributes arguments from a type 171 | * @param t type to query 172 | * @param name name of the attribute to get 173 | * @param placeholder_ values to return if the attribute doesn't exist 174 | * @returns attribute arguments or placeholder values 175 | */ 176 | std::vector attribute(type_info t, std::string_view name, std::vector placeholder_ = {}); 177 | 178 | /** 179 | * @brief Get dynamic type information for a known type. 180 | * @see reflect 181 | */ 182 | template 183 | auto reflect(){ 184 | static auto ret = detail::reflect_helper::reflect(); 185 | return ret; 186 | } 187 | 188 | /** 189 | * @brief Check if a type has a specified base class. 190 | * @param type type to check 191 | * @param base base to check against 192 | */ 193 | bool has_base(class_info type, class_info base) noexcept; 194 | 195 | template 196 | struct derived_info; 197 | 198 | /** 199 | * @brief Helper function to get information about all classes deriving from a common parent. 200 | * @warning this can be an expensive operation 201 | * @return type information for all reflected derived classes 202 | */ 203 | template 204 | std::vector> reflect_all_derived(); 205 | 206 | /** 207 | * @brief Helper class for storing information about a specific heirarchy of types 208 | * @tparam Base base class for stored class type 209 | */ 210 | template 211 | struct derived_info{ 212 | public: 213 | template 214 | derived_info(meta::type){ 215 | static_assert(std::is_base_of_v); 216 | m_info = reflect(); 217 | } 218 | 219 | template 220 | derived_info(const derived_info &other) noexcept{ 221 | static_assert(std::is_base_of_v); 222 | m_info = other.m_info; 223 | } 224 | 225 | derived_info(class_info cls){ 226 | if(!has_base(cls, base_info())){ 227 | throw std::runtime_error("class type is not derived from base"); 228 | } 229 | 230 | m_info = cls; 231 | } 232 | 233 | template 234 | derived_info &operator=(const derived_info &other) noexcept{ 235 | static_assert(std::is_base_of_v); 236 | 237 | if(this != &other){ 238 | m_info = other.m_info; 239 | } 240 | 241 | return *this; 242 | } 243 | 244 | operator class_info() const noexcept{ return m_info; } 245 | 246 | class_info operator->() const noexcept{ return m_info; } 247 | 248 | static class_info base_info(){ 249 | static const class_info ret = reflect(); 250 | return ret; 251 | } 252 | 253 | private: 254 | struct unsafe_tag{}; 255 | 256 | derived_info(unsafe_tag, class_info cls) noexcept 257 | : m_info(cls){} 258 | 259 | class_info m_info; 260 | 261 | friend std::vector> reflect_all_derived(); 262 | }; 263 | 264 | template 265 | std::vector> reflect_all_derived(){ 266 | static auto all_cls = reflect_all_classes(); 267 | static auto base = reflect(); 268 | 269 | std::vector> ret; 270 | 271 | using info = derived_info; 272 | 273 | for(auto cls : all_cls){ 274 | if(has_base(cls, base)){ 275 | ret.emplace_back(info(typename info::unsafe_tag{}, cls)); 276 | } 277 | } 278 | 279 | return ret; 280 | } 281 | 282 | namespace detail{ 283 | template 284 | struct arg_type_helper{ using type = T; }; 285 | 286 | template 287 | struct arg_type_helper>{ using type = T; }; 288 | } 289 | 290 | /** 291 | * @brief Helper type for dynamically passing arguments. 292 | */ 293 | struct args_pack_base{ 294 | virtual std::size_t size() const noexcept = 0; 295 | //virtual void *arg(std::size_t idx) const noexcept = 0; 296 | virtual type_info arg_type(std::size_t idx) const noexcept = 0; 297 | virtual class_info this_type() const noexcept = 0; 298 | }; 299 | 300 | template 301 | struct args_pack: args_pack_base{ 302 | public: 303 | using arg_types = metapp::types; 304 | 305 | template 306 | explicit args_pack(UArgs &&... args) 307 | : args_pack(std::make_index_sequence(), std::forward(args)...) 308 | {} 309 | 310 | std::size_t size() const noexcept override{ return sizeof...(Args); } 311 | //void *arg(std::size_t idx) const noexcept{ return idx >= size() ? nullptr : m_ptrs[idx]; } 312 | type_info arg_type(std::size_t idx) const noexcept{ return idx >= size() ? nullptr : m_types[idx]; } 313 | 314 | class_info this_type() const noexcept override{ return reflect>(); } 315 | 316 | template 317 | decltype(auto) apply(Fn &&f){ 318 | return apply_impl(std::forward(f), std::make_index_sequence()); 319 | } 320 | 321 | private: 322 | template 323 | args_pack(std::index_sequence, UArgs &&... args) 324 | : m_vals(std::forward_as_tuple(std::forward(args)...)) 325 | , m_types{ reflect>()... } 326 | {} 327 | 328 | template 329 | decltype(auto) apply_impl(Fn &&f, std::index_sequence){ 330 | return std::forward(f)(static_cast(std::get(m_vals))...); 331 | } 332 | 333 | using value_types = metapp::types...>; 334 | std::tuple m_vals; 335 | type_info m_types[sizeof...(Args)]; 336 | }; 337 | 338 | /** 339 | * @brief Pack a set of arguments for passing dynamically. 340 | * @warning Arguments passed to this function must stay alive until the returned pack is destroyed. 341 | * @param args arguments to pack 342 | * @returns newly created argument pack 343 | */ 344 | template 345 | auto pack_args(Args &&... args){ 346 | return args_pack::type...>(std::forward(args)...); 347 | } 348 | 349 | namespace detail{ 350 | template 351 | struct info_helper_base: Helper{ 352 | std::string_view name() const noexcept override{ return metapp::type_name; } 353 | std::size_t size() const noexcept override{ return sizeof(T); } 354 | std::size_t alignment() const noexcept override{ return alignof(T); } 355 | void destroy(void *p) const noexcept override{ std::destroy_at(reinterpret_cast(p)); } 356 | std::type_index type_index() const noexcept override{ return typeid(T); } 357 | }; 358 | 359 | struct ref_info_helper: type_info_helper{ 360 | virtual type_info refered() const noexcept = 0; 361 | 362 | void *construct(void *p, args_pack_base *args) const override{ 363 | if(args->size() != 0) return nullptr; 364 | return p; 365 | } 366 | }; 367 | 368 | struct ptr_info_helper: type_info_helper{ 369 | virtual type_info pointed() const noexcept = 0; 370 | 371 | void *construct(void *p, args_pack_base *args) const override{ 372 | if(args->size() != 0) return nullptr; 373 | return p; 374 | } 375 | }; 376 | 377 | struct fn_ptr_info_helper: type_info_helper{ 378 | virtual type_info result() const noexcept = 0; 379 | virtual std::size_t num_parameters() const noexcept= 0; 380 | virtual type_info parameter(std::size_t idx) const noexcept = 0; 381 | }; 382 | 383 | type_info void_info() noexcept; 384 | int_info int_info(std::size_t bits, bool is_signed) noexcept; 385 | num_info float_info(std::size_t bits) noexcept; 386 | 387 | struct num_info_helper: type_info_helper{ 388 | virtual bool is_floating_point() const noexcept = 0; 389 | virtual bool is_integer() const noexcept = 0; 390 | void *construct(void *p, args_pack_base *args) const override{ 391 | if(args->size() != 0) return nullptr; 392 | return p; 393 | } 394 | }; 395 | 396 | struct int_info_helper: num_info_helper{ 397 | bool is_floating_point() const noexcept override{ return false; } 398 | bool is_integer() const noexcept override{ return true; } 399 | 400 | virtual bool is_signed() const noexcept = 0; 401 | }; 402 | 403 | template 404 | struct float_info_helper_impl: info_helper_base, num_info_helper>{ 405 | bool is_floating_point() const noexcept override{ return true; } 406 | bool is_integer() const noexcept override{ return false; } 407 | }; 408 | 409 | template 410 | struct int_info_helper_impl: info_helper_base, int_info_helper>{ 411 | bool is_signed() const noexcept override{ return std::is_signed_v; } 412 | }; 413 | 414 | struct function_info_helper{ 415 | virtual std::string_view name() const noexcept = 0; 416 | virtual type_info result_type() const noexcept = 0; 417 | virtual std::size_t num_params() const noexcept = 0; 418 | virtual std::string_view param_name(std::size_t idx) const noexcept = 0; 419 | virtual type_info param_type(std::size_t idx) const noexcept = 0; 420 | }; 421 | 422 | struct class_member_helper{ 423 | virtual std::string_view name() const noexcept = 0; 424 | virtual type_info type() const noexcept = 0; 425 | virtual std::size_t num_attributes() const noexcept = 0; 426 | virtual attribute_info attribute(std::size_t idx) const noexcept = 0; 427 | virtual void *get(void *self) const noexcept = 0; 428 | }; 429 | 430 | struct class_method_helper{ 431 | virtual std::string_view name() const noexcept = 0; 432 | virtual type_info result_type() const noexcept = 0; 433 | virtual std::size_t num_params() const noexcept = 0; 434 | virtual std::string_view param_name(std::size_t idx) const noexcept = 0; 435 | virtual type_info param_type(std::size_t idx) const noexcept = 0; 436 | }; 437 | 438 | template 439 | struct class_member_impl final: class_member_helper{ 440 | using member_info = metapp::class_member; 441 | 442 | std::string_view name() const noexcept override{ return member_info::name; } 443 | 444 | type_info type() const noexcept override{ 445 | static const auto ret = reflect(); 446 | return ret; 447 | } 448 | 449 | std::size_t num_attributes() const noexcept override{ 450 | return member_info::attributes::size; 451 | } 452 | 453 | attribute_info attribute(std::size_t idx) const noexcept override{ 454 | using attributes = typename member_info::attributes; 455 | 456 | if(idx >= num_attributes()) return nullptr; 457 | 458 | attribute_info ret = nullptr; 459 | 460 | metapp::for_all_i([idx, &ret](auto info_type, auto info_idx){ 461 | if(ret || idx != info_idx){ 462 | return; 463 | } 464 | 465 | using info = metapp::get_t; 466 | 467 | struct attribute_info_impl: attribute_info_helper{ 468 | std::string_view scope() const noexcept override{ return info::scope; } 469 | std::string_view name() const noexcept override{ return info::name; } 470 | std::size_t num_args() const noexcept override{ return info::args::size; } 471 | std::string_view arg(std::size_t idx0) const noexcept override{ 472 | using arguments = typename info::args; 473 | 474 | if(idx0 >= num_args()) return ""; 475 | 476 | std::string_view ret0; 477 | 478 | metapp::for_all_i([idx0, &ret0](auto info_type, auto info_idx){ 479 | if(!ret0.empty() || idx0 != info_idx){ 480 | return; 481 | } 482 | 483 | using argument = metapp::get_t; 484 | 485 | ret0 = argument::value; 486 | }); 487 | 488 | return ret0; 489 | } 490 | } static ret_val; 491 | 492 | ret = &ret_val; 493 | }); 494 | 495 | return ret; 496 | } 497 | 498 | void *get(void *self) const noexcept override{ 499 | auto p = reinterpret_cast(self); 500 | return &member_info::get(*p); 501 | } 502 | }; 503 | 504 | template 505 | struct class_method_impl final: class_method_helper{ 506 | using method_info = metapp::class_method; 507 | 508 | std::string_view name() const noexcept override{ 509 | return method_info::name; 510 | } 511 | 512 | type_info result_type() const noexcept override{ 513 | static const auto ret = reflect(); 514 | return ret; 515 | } 516 | 517 | std::size_t num_params() const noexcept override{ 518 | std::size_t ret = 0; 519 | metapp::for_all([&](auto info_type){ 520 | using info = metapp::get_t; 521 | if constexpr(info::is_variadic){ 522 | ret += info::type::size; 523 | } 524 | else{ 525 | ++ret; 526 | } 527 | }); 528 | 529 | return ret; 530 | } 531 | 532 | std::string_view param_name(std::size_t idx) const noexcept override{ 533 | if(idx >= num_params()) return ""; 534 | std::string_view ret; 535 | std::size_t offset = 0; 536 | metapp::for_all([&](auto info_type){ 537 | using info = metapp::get_t; 538 | if constexpr(info::is_variadic){ 539 | metapp::for_all([&](auto){ 540 | if(offset++ != idx) return; 541 | ret = info::name; 542 | }); 543 | } 544 | else{ 545 | if(offset++ != idx) return; 546 | ret = info::name; 547 | } 548 | }); 549 | return ret; 550 | } 551 | 552 | type_info param_type(std::size_t idx) const noexcept override{ 553 | if(idx >= num_params()) return nullptr; 554 | type_info ret = nullptr; 555 | std::size_t offset = 0; 556 | metapp::for_all([&](auto info_type){ 557 | using info = metapp::get_t; 558 | 559 | if constexpr(info::is_variadic){ 560 | metapp::for_all([&](auto inner_type){ 561 | using inner = metapp::get_t; 562 | if(offset++ != idx) return; 563 | static const auto reflected = reflect(); 564 | ret = reflected; 565 | }); 566 | } 567 | else{ 568 | if(offset != idx) return; 569 | static const auto reflected = reflect(); 570 | ret = reflected; 571 | } 572 | }); 573 | return ret; 574 | } 575 | }; 576 | 577 | struct class_info_helper: type_info_helper{ 578 | virtual std::size_t num_methods() const noexcept = 0; 579 | virtual const class_method_helper *method(std::size_t idx) const noexcept = 0; 580 | 581 | virtual std::size_t num_members() const noexcept = 0; 582 | virtual const class_member_helper *member(std::size_t idx) const noexcept = 0; 583 | 584 | virtual std::size_t num_bases() const noexcept = 0; 585 | virtual class_info base(std::size_t i) const noexcept = 0; 586 | 587 | virtual void *cast_to_base(void *self, std::size_t idx) const noexcept = 0; 588 | 589 | template 590 | T *cast_to(void *self_void, const class_info to = reflect()) const noexcept{ 591 | if(this == to){ 592 | return reinterpret_cast(self_void); 593 | } 594 | 595 | const auto num_bases_ = num_bases(); 596 | 597 | for(std::size_t i = 0; i < num_bases_; i++){ 598 | auto base_ = base(i); 599 | auto as_base = cast_to_base(self_void, i); 600 | if(base_ == to){ 601 | return reinterpret_cast(as_base); 602 | } 603 | else{ 604 | auto as_inner_base = base_->cast_to(as_base, to); 605 | if(as_inner_base) return as_inner_base; 606 | } 607 | } 608 | 609 | return nullptr; 610 | } 611 | }; 612 | 613 | template 614 | struct class_info_impl final: info_helper_base{ 615 | using class_meta = metapp::class_info; 616 | 617 | std::size_t m_num_bases = 0; 618 | 619 | class_info_impl(){ 620 | metapp::for_all>([&](auto base_info){ 621 | using info = metapp::get_t; 622 | if constexpr(info::is_variadic){ 623 | m_num_bases += info::type::size; 624 | } 625 | else{ 626 | ++m_num_bases; 627 | } 628 | }); 629 | 630 | register_type(this, true); 631 | } 632 | 633 | std::size_t num_attributes() const noexcept override{ return class_meta::attributes::size; } 634 | 635 | attribute_info attribute(std::size_t idx) const noexcept override{ 636 | using attributes = typename class_meta::attributes; 637 | 638 | if(idx >= num_attributes()) return nullptr; 639 | 640 | attribute_info ret = nullptr; 641 | 642 | metapp::for_all_i([idx, &ret](auto info_type, auto info_idx){ 643 | if(ret || idx != info_idx){ 644 | return; 645 | } 646 | 647 | using info = metapp::get_t; 648 | 649 | struct attribute_info_impl: attribute_info_helper{ 650 | std::string_view scope() const noexcept override{ return info::scope; } 651 | std::string_view name() const noexcept override{ return info::name; } 652 | std::size_t num_args() const noexcept override{ return info::args::size; } 653 | std::string_view arg(std::size_t idx0) const noexcept override{ 654 | using arguments = typename info::args; 655 | 656 | if(idx0 >= num_args()) return ""; 657 | 658 | std::string_view ret0; 659 | 660 | metapp::for_all_i([idx0, &ret0](auto info_type, auto info_idx){ 661 | if(!ret0.empty() || idx0 != info_idx){ 662 | return; 663 | } 664 | 665 | using argument = metapp::get_t; 666 | 667 | ret0 = argument::value; 668 | }); 669 | 670 | return ret0; 671 | } 672 | } static ret_val; 673 | 674 | ret = &ret_val; 675 | }); 676 | 677 | return ret; 678 | } 679 | 680 | std::size_t num_methods() const noexcept override{ return class_meta::methods::size; } 681 | 682 | const class_method_helper *method(std::size_t idx) const noexcept override{ 683 | if(idx >= num_methods()) return nullptr; 684 | 685 | const class_method_helper *ret = nullptr; 686 | 687 | metapp::for_all_i>([idx, &ret](auto info_type, auto info_idx){ 688 | if(idx != info_idx) return; 689 | 690 | static const class_method_impl> method_impl; 691 | ret = &method_impl; 692 | }); 693 | 694 | return ret; 695 | } 696 | 697 | std::size_t num_members() const noexcept override{ return class_meta::members::size; } 698 | 699 | const class_member_helper *member(std::size_t idx) const noexcept override{ 700 | if(idx >= num_members()) return nullptr; 701 | 702 | const class_member_helper *ret = nullptr; 703 | 704 | metapp::for_all_i>([idx, &ret](auto info_type, auto info_idx){ 705 | if(idx != info_idx) return; 706 | 707 | static const class_member_impl> member_impl; 708 | ret = &member_impl; 709 | }); 710 | 711 | return ret; 712 | } 713 | 714 | std::size_t num_bases() const noexcept override{ return m_num_bases; } 715 | 716 | class_info base(std::size_t idx) const noexcept override{ 717 | if(idx >= num_bases()) return nullptr; 718 | 719 | class_info ret = nullptr; 720 | 721 | std::size_t cur_idx = 0; 722 | 723 | metapp::for_all>([idx, &cur_idx, &ret](auto base_info){ 724 | using info = metapp::get_t; 725 | 726 | if constexpr(info::is_variadic){ 727 | metapp::for_all([idx, &cur_idx, &ret](auto inner_info){ 728 | using info_inner = metapp::get_t; 729 | 730 | if(idx == cur_idx++){ 731 | ret = reflect(); 732 | } 733 | }); 734 | } 735 | else{ 736 | if(idx == cur_idx++){ 737 | ret = reflect(); 738 | } 739 | } 740 | }); 741 | 742 | return ret; 743 | } 744 | 745 | void *cast_to_base(void *self_void, std::size_t idx) const noexcept override{ 746 | if(idx >= num_bases()) return nullptr; 747 | 748 | void *ret = nullptr; 749 | 750 | auto self = reinterpret_cast(self_void); 751 | 752 | std::size_t cur_idx = 0; 753 | 754 | metapp::for_all>([idx, self, &cur_idx, &ret](auto base_info){ 755 | using info = metapp::get_t; 756 | 757 | if constexpr(info::access != metapp::access_kind::public_){ 758 | return; 759 | } 760 | else{ 761 | if(ret) return; 762 | if constexpr(info::is_variadic){ 763 | metapp::for_all([idx, self, &cur_idx, &ret](auto inner_info){ 764 | using info_inner = metapp::get_t; 765 | 766 | if(idx != cur_idx++){ 767 | return; 768 | } 769 | 770 | ret = dynamic_cast(self); 771 | }); 772 | } 773 | else{ 774 | if(idx != cur_idx++){ 775 | return; 776 | } 777 | 778 | ret = dynamic_cast(self); 779 | } 780 | } 781 | }); 782 | 783 | return ret; 784 | } 785 | 786 | void *construct(void *p, args_pack_base *args) const override{ 787 | if constexpr(std::is_abstract_v){ 788 | return nullptr; 789 | } 790 | else{ 791 | void *ret = nullptr; 792 | 793 | metapp::for_all>([&](auto info_type){ 794 | using ctor_info = metapp::get_t; 795 | if constexpr(ctor_info::is_accessable){ 796 | if(ret) return; 797 | 798 | if constexpr(ctor_info::params::size == 0){ 799 | if(args->size() != 0) return; 800 | else ret = new(p) T(); 801 | } 802 | else{ 803 | using param_types = metapp::param_types; 804 | using args_derived = metapp::instantiate; 805 | 806 | auto args_ptr = dynamic_cast(args); 807 | if(!args_ptr){ 808 | return; 809 | } 810 | 811 | args_ptr->apply([&](auto &&... args){ 812 | ret = new(p) T(std::forward(args)...); 813 | }); 814 | } 815 | } 816 | }); 817 | 818 | return ret; 819 | } 820 | } 821 | }; 822 | 823 | struct enum_value_helper{ 824 | virtual std::string_view name() const noexcept = 0; 825 | virtual std::uint64_t value() const noexcept = 0; 826 | }; 827 | 828 | template 829 | struct enum_value_impl: enum_value_helper{ 830 | using info = metapp::enum_value; 831 | std::string_view name() const noexcept override{ return info::name; } 832 | std::uint64_t value() const noexcept override{ return info::value; } 833 | }; 834 | 835 | struct enum_info_helper: type_info_helper{ 836 | virtual std::size_t num_values() const noexcept = 0; 837 | virtual const enum_value_helper *value(std::size_t idx) const noexcept = 0; 838 | }; 839 | 840 | template 841 | struct enum_info_impl: info_helper_base{ 842 | using enum_meta = metapp::enum_info; 843 | 844 | enum_info_impl(){ register_type(this, true); } 845 | 846 | std::size_t num_values() const noexcept override{ return enum_meta::values::size; } 847 | 848 | const enum_value_helper *value(std::size_t idx) const noexcept override{ 849 | if(idx >= num_values()) return nullptr; 850 | 851 | const enum_value_helper *ret = nullptr; 852 | 853 | metapp::for_all_i([idx, &ret](auto info_type, auto info_idx){ 854 | if(idx != info_idx) return; 855 | 856 | static const enum_value_impl> reflected; 857 | ret = &reflected; 858 | }); 859 | 860 | return ret; 861 | } 862 | 863 | void *construct(void *p, args_pack_base *args) const override{ 864 | if(args->size() != 0) return nullptr; 865 | return p; 866 | } 867 | }; 868 | 869 | bool register_type(type_info info, bool overwrite = false); 870 | 871 | template 872 | struct reflect_simple{ 873 | static auto reflect(){ 874 | if constexpr(std::is_class_v){ 875 | struct class_info_impl final: info_helper_base{ 876 | class_info_impl(){ register_type(this); } 877 | 878 | std::size_t num_methods() const noexcept override{ return 0; } 879 | const class_method_helper *method(std::size_t) const noexcept override{ return nullptr; } 880 | 881 | std::size_t num_members() const noexcept override{ return 0; } 882 | const class_member_helper *member(std::size_t) const noexcept override{ return nullptr; } 883 | 884 | std::size_t num_bases() const noexcept override{ return 0; } 885 | class_info base(std::size_t) const noexcept override{ return nullptr; } 886 | 887 | void *cast_to_base(void *self, std::size_t) const noexcept override{ return nullptr; } 888 | 889 | void *construct(void *p, args_pack_base *args) const override{ 890 | return nullptr; 891 | } 892 | } static ret; 893 | return &ret; 894 | } 895 | else{ 896 | struct type_info_impl final: info_helper_base{ 897 | type_info_impl(){ register_type(this); } 898 | } static ret; 899 | return &ret; 900 | } 901 | } 902 | }; 903 | 904 | template 905 | struct reflect_info; 906 | 907 | template 908 | struct reflect_info>>{ 909 | static auto reflect(){ 910 | static const enum_info_impl ret; 911 | return &ret; 912 | } 913 | }; 914 | 915 | template 916 | struct reflect_info>>{ 917 | static auto reflect(){ 918 | static const class_info_impl ret; 919 | return &ret; 920 | } 921 | }; 922 | 923 | template 924 | struct reflect_helper>>{ 925 | static type_info reflect(){ 926 | struct ref_info_impl final: ref_info_helper{ 927 | ref_info_impl(){ register_type(this); } 928 | std::string_view name() const noexcept override{ return metapp::type_name; } 929 | std::size_t size() const noexcept override{ return sizeof(void*); } 930 | std::size_t alignment() const noexcept override{ return alignof(void*); } 931 | void destroy(void *p) const noexcept override{ } 932 | std::type_index type_index() const noexcept override{ return typeid(T); } 933 | type_info refered() const noexcept override{ static auto ret = reflpp::reflect>(); return ret; } 934 | } static ret; 935 | return &ret; 936 | } 937 | }; 938 | 939 | template 940 | struct reflect_helper && !std::is_function_v>>>{ 941 | static type_info reflect(){ 942 | struct ptr_info_impl final: ptr_info_helper{ 943 | ptr_info_impl(){ register_type(this); } 944 | std::string_view name() const noexcept override{ return metapp::type_name; } 945 | std::size_t size() const noexcept override{ return sizeof(T); } 946 | std::size_t alignment() const noexcept override{ return alignof(T); } 947 | void destroy(void *p) const noexcept override{ } 948 | std::type_index type_index() const noexcept override{ return typeid(T); } 949 | type_info pointed() const noexcept override{ static auto ret = reflpp::reflect>(); return ret; } 950 | } static ret; 951 | return &ret; 952 | } 953 | }; 954 | 955 | template 956 | struct reflect_helper{ 957 | static fn_ptr_info reflect(){ 958 | struct fn_ptr_info_impl final: fn_ptr_info_helper{ 959 | fn_ptr_info_impl(){ register_type(this); } 960 | std::string_view name() const noexcept override{ return metapp::type_name; } 961 | std::size_t size() const noexcept override{ return sizeof(Ret(*)(Args...)); } 962 | std::size_t alignment() const noexcept override{ return alignof(Ret(*)(Args...)); } 963 | void destroy(void *p) const noexcept override{} 964 | std::type_index type_index() const noexcept override{ return typeid(Ret(*)(Args...)); } 965 | type_info result() const noexcept override{ return reflpp::reflect(); } 966 | std::size_t num_parameters() const noexcept override{ return sizeof...(Args); } 967 | type_info parameter(std::size_t idx) const noexcept override{ 968 | if(idx >= sizeof...(Args)){ 969 | return nullptr; 970 | } 971 | 972 | type_info ret = nullptr; 973 | 974 | metapp::for_all_i>([idx, &ret](auto info_type, auto index_value){ 975 | using type = metapp::get_t; 976 | constexpr auto index = metapp::get_v; 977 | 978 | if(ret) return; 979 | else if(index == idx){ 980 | ret = reflpp::reflect(); 981 | } 982 | }); 983 | 984 | return ret; 985 | } 986 | 987 | void *construct(void *p, args_pack_base *args) const override{ 988 | if(args->size() != 0) return nullptr; 989 | return new(p) (Ret(*)(Args...)); 990 | } 991 | } static ret; 992 | return &ret; 993 | } 994 | }; 995 | 996 | template 997 | struct reflect_helper>>{ 998 | static type_info reflect(){ return void_info(); } 999 | }; 1000 | 1001 | template 1002 | struct reflect_helper>>{ 1003 | static reflpp::int_info reflect(){ return detail::int_info(sizeof(Int) * CHAR_BIT, std::is_signed_v); } 1004 | }; 1005 | 1006 | template 1007 | struct reflect_helper>>{ 1008 | static num_info reflect(){ return detail::float_info(sizeof(Float) * CHAR_BIT); } 1009 | }; 1010 | 1011 | template 1012 | struct reflect_helper>>{ 1013 | static class_info reflect(){ 1014 | if(auto ret = reflect_class(metapp::type_name); ret){ 1015 | if constexpr(metapp::has_info){ 1016 | auto elaborated = dynamic_cast*>(ret); 1017 | if(!elaborated){ 1018 | return reflect_info::reflect(); 1019 | } 1020 | } 1021 | 1022 | return ret; 1023 | } 1024 | else{ 1025 | if constexpr(metapp::has_info){ 1026 | return reflect_info::reflect(); 1027 | } 1028 | else{ 1029 | return reflect_simple::reflect(); 1030 | } 1031 | } 1032 | } 1033 | }; 1034 | 1035 | template 1036 | struct reflect_helper>>{ 1037 | static enum_info reflect(){ return reflect_enum(metapp::type_name); } 1038 | }; 1039 | 1040 | template 1041 | REFLCPP_API extern type_info type_export(); 1042 | 1043 | template 1044 | REFLCPP_API extern function_info function_export(); 1045 | 1046 | using type_export_fn = type_info(*)(); 1047 | using function_export_fn = function_info(*)(); 1048 | 1049 | static void *aligned_alloc(std::size_t align, std::size_t len){ 1050 | #ifdef __linux__ 1051 | return ::aligned_alloc(align, len); 1052 | #elif defined(_WIN32) 1053 | return _aligned_malloc(len, align); 1054 | #else 1055 | return std::aligned_alloc(align, len); 1056 | #endif 1057 | } 1058 | 1059 | static void aligned_free(void *p){ 1060 | #ifdef __linux__ 1061 | ::free(p); 1062 | #elif defined(_WIN32) 1063 | _aligned_free(p); 1064 | #else 1065 | std::free(p); 1066 | #endif 1067 | } 1068 | } 1069 | 1070 | /** 1071 | * @brief Class for dynamically creating values of statically-unknown types. 1072 | * @tparam Base base class of all created values or `void` for any value. 1073 | */ 1074 | template class AllocT = std::allocator> 1075 | class alignas(16) value{ 1076 | public: 1077 | using info_type = std::conditional_t, type_info, class_info>; 1078 | 1079 | value() = default; 1080 | 1081 | template 1082 | value(metapp::type, Args &&... args) 1083 | : m_type(reflect()) 1084 | { 1085 | if constexpr(sizeof(T) <= 16 && alignof(T) <= 16){ 1086 | new(m_storage.bytes) T(std::forward(args)...); 1087 | m_destroy_fn = [](void *mem, info_type){ 1088 | auto ptr = reinterpret_cast(mem); 1089 | std::destroy_at(ptr); 1090 | }; 1091 | } 1092 | else{ 1093 | using aligned_storage = std::aligned_storage_t; 1094 | AllocT alloc; 1095 | 1096 | m_storage.pointer = alloc.allocate(1); 1097 | m_destroy_fn = [](void *mem, info_type){ 1098 | AllocT alloc; 1099 | 1100 | auto ptr = reinterpret_cast(mem); 1101 | std::destroy_at(ptr); 1102 | alloc.deallocate(reinterpret_cast(mem), 1); 1103 | }; 1104 | } 1105 | } 1106 | 1107 | template 1108 | explicit value(info_type type_, Args &&... args) 1109 | : m_type(nullptr) 1110 | { 1111 | construct(type_, std::forward(args)...); 1112 | } 1113 | 1114 | value(const value&) = delete; 1115 | 1116 | template class AllocU> 1117 | value(value &&other) noexcept{ 1118 | if constexpr(std::is_class_v){ 1119 | static_assert(std::is_base_of_v); 1120 | } 1121 | else if constexpr(!std::is_same_v){ 1122 | static_assert(std::is_same_v); 1123 | } 1124 | 1125 | m_type = std::exchange(other.m_type, nullptr); 1126 | m_destroy_fn = std::exchange(other.m_destroy_fn, [](auto...){}); 1127 | 1128 | if(!m_type){ 1129 | return; 1130 | } 1131 | 1132 | if(m_type->size() <= 16 && m_type->alignment() <= 16){ 1133 | std::memcpy(m_storage.bytes, other.m_storage.bytes, m_type->size()); 1134 | //std::memset(other.m_storage.bytes, 0, m_type->size()); 1135 | } 1136 | else{ 1137 | m_storage.pointer = std::exchange(other.m_storage.pointer, nullptr); 1138 | } 1139 | } 1140 | 1141 | ~value(){ 1142 | destroy(); 1143 | } 1144 | 1145 | value &operator=(const value&) = delete; 1146 | 1147 | template class AllocU> 1148 | value &operator=(value &&other) noexcept{ 1149 | if constexpr(std::is_same_v){ 1150 | if(this == &other) return *this; 1151 | } 1152 | else if constexpr(std::is_class_v){ 1153 | static_assert(std::is_base_of_v); 1154 | } 1155 | else if constexpr(!std::is_same_v){ 1156 | static_assert(std::is_same_v); 1157 | } 1158 | 1159 | destroy(); 1160 | 1161 | m_type = std::exchange(other.m_type, nullptr); 1162 | m_destroy_fn = std::exchange(other.m_destroy_fn, [](auto...){}); 1163 | 1164 | if(m_type){ 1165 | if(m_type->size() <= 16 && m_type->alignment() <= 16){ 1166 | std::memcpy(m_storage.bytes, other.m_storage.bytes, m_type->size()); 1167 | //std::memset(other.m_storage.bytes, 0, m_type->size()); 1168 | } 1169 | else{ 1170 | m_storage.pointer = std::exchange(other.m_storage.pointer, nullptr); 1171 | } 1172 | } 1173 | 1174 | return *this; 1175 | } 1176 | 1177 | Base *operator->() noexcept{ return as(); } 1178 | const Base *operator->() const noexcept{ return as(); } 1179 | 1180 | /** 1181 | * @brief Check that a value is contained in the object. 1182 | */ 1183 | bool is_valid() const noexcept{ return !!m_type; } 1184 | 1185 | /** 1186 | * @brief Get the type of the contained value. 1187 | */ 1188 | info_type type() const noexcept{ return m_type; } 1189 | 1190 | /** 1191 | * @brief Try to get the value as a specified type. 1192 | */ 1193 | template 1194 | T *as() noexcept{ 1195 | if(!is_valid()){ 1196 | return nullptr; 1197 | } 1198 | 1199 | if constexpr(std::is_class_v){ 1200 | return m_type->template cast_to(ptr()); 1201 | } 1202 | else if constexpr(std::is_class_v){ 1203 | auto cls = dynamic_cast(m_type); 1204 | if(!cls) return nullptr; 1205 | return cls->template cast_to(ptr()); 1206 | } 1207 | else{ 1208 | return m_type == reflect() ? reinterpret_cast(ptr()) : nullptr; 1209 | } 1210 | } 1211 | 1212 | /** 1213 | * @brief Try to get the value as a specified type. 1214 | */ 1215 | template 1216 | const T *as() const noexcept{ 1217 | if(!is_valid()){ 1218 | return nullptr; 1219 | } 1220 | 1221 | if constexpr(std::is_class_v){ 1222 | return m_type->template cast_to(ptr()); 1223 | } 1224 | else if constexpr(std::is_class_v){ 1225 | auto cls = dynamic_cast(m_type); 1226 | if(!cls) return nullptr; 1227 | return cls->template cast_to(ptr()); 1228 | } 1229 | else{ 1230 | return m_type == reflect() ? reinterpret_cast(ptr()) : nullptr; 1231 | } 1232 | } 1233 | 1234 | private: 1235 | void destroy(){ 1236 | if(!m_type) return; 1237 | 1238 | m_destroy_fn(ptr(), m_type); 1239 | 1240 | m_type = nullptr; 1241 | m_destroy_fn = [](auto...){}; 1242 | } 1243 | 1244 | template 1245 | void construct(info_type type_, Args &&... args){ 1246 | destroy(); 1247 | 1248 | auto pack = pack_args(std::forward(args)...); 1249 | 1250 | if(type_->size() <= 16 && type_->alignment() <= 16){ 1251 | std::memset(m_storage.bytes, 0, type_->size()); 1252 | if(!type_->construct(m_storage.bytes, &pack)){ 1253 | throw std::runtime_error("Could not construct small value"); 1254 | } 1255 | 1256 | m_destroy_fn = [](void *mem, info_type info){ 1257 | info->destroy(mem); 1258 | }; 1259 | } 1260 | else{ 1261 | m_storage.pointer = detail::aligned_alloc(type_->alignment(), type_->size()); 1262 | if(!type_->construct(m_storage.pointer, &pack)){ 1263 | throw std::runtime_error("Could not construct value"); 1264 | } 1265 | 1266 | m_destroy_fn = [](void *mem, info_type info){ 1267 | info->destroy(mem); 1268 | detail::aligned_free(mem); 1269 | }; 1270 | } 1271 | 1272 | m_type = type_; 1273 | } 1274 | 1275 | void *ptr() noexcept{ 1276 | if(!m_type) return nullptr; 1277 | 1278 | if(m_type->size() <= 16 && m_type->alignment() <= 16){ 1279 | return m_storage.bytes; 1280 | } 1281 | else{ 1282 | return m_storage.pointer; 1283 | } 1284 | } 1285 | 1286 | const void *ptr() const noexcept{ 1287 | if(!m_type) return nullptr; 1288 | 1289 | if(m_type->size() <= 16 && m_type->alignment() <= 16){ 1290 | return m_storage.bytes; 1291 | } 1292 | else{ 1293 | return m_storage.pointer; 1294 | } 1295 | } 1296 | 1297 | info_type m_type = nullptr; 1298 | void(*m_destroy_fn)(void*, info_type); 1299 | 1300 | union storage_t{ 1301 | void *pointer; 1302 | unsigned char bytes alignas(16)[16]; 1303 | } m_storage; 1304 | 1305 | template class AllocU> 1306 | friend class value; 1307 | }; 1308 | } 1309 | 1310 | #ifndef METACPP_NO_NAMESPACE_ALIAS 1311 | namespace METACPP_REFL_NAMESPACE = reflpp; 1312 | #endif 1313 | 1314 | /** 1315 | * @} 1316 | */ 1317 | 1318 | #endif // !METACPP_REFL_HPP 1319 | --------------------------------------------------------------------------------