From 3c41a8a2b762b8843a9f3d0ca62a691c55ae8c6f Mon Sep 17 00:00:00 2001 From: RedSkittleFox <RedSkittleFox@gmail.com> Date: Wed, 17 Jan 2024 15:48:51 +0100 Subject: [PATCH 1/7] v3-update --- .gitignore | 5 + CMakeLists.txt | 25 +- CMakeSettings.json | 62 ++++ include/fox/reflexpr.hpp | 608 ++++++++++++++------------------ sample/CMakeLists.txt | 2 +- sample/main.cpp | 95 ++--- test/CMakeLists.txt | 77 ++++ test/reflexpr_test.cc | 240 +++++++++++++ test/reflexpr_test_generator.py | 56 +++ 9 files changed, 756 insertions(+), 414 deletions(-) create mode 100644 CMakeSettings.json create mode 100644 test/CMakeLists.txt create mode 100644 test/reflexpr_test.cc create mode 100644 test/reflexpr_test_generator.py diff --git a/.gitignore b/.gitignore index a132c2c..60718d0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,11 @@ bin/* bin-etc/* lib/* output/* +out/* +.vs/* + +# Autogenerated +test/reflexpr_test_types.inl # Prerequisites *.d diff --git a/CMakeLists.txt b/CMakeLists.txt index 60cc2d8..354697c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,6 @@ if (PROJECT_IS_TOP_LEVEL) endif() set_property(GLOBAL PROPERTY USE_FOLDERS ON) -enable_testing() include(CheckIPOSupported) check_ipo_supported(RESULT IPO_SUPPORTED OUTPUT IPO_NOT_SUPPORTED_ERROR LANGUAGES CXX) @@ -33,13 +32,25 @@ add_subdirectory("include") add_library(fox::reflexpr ALIAS reflexpr) -option(sample ON) -# option(test ON) +option( + FOX_REFLEXPR_BUILD_SAMPLES + "If samples are built." + ON +) -if (sample) +option( + FOX_REFLEXPR_BUILD_TESTS + "If unit tests are built" + ON +) + +option(FOX_REFLEXPR_BUILD_SAMPLES ON) +option(FOX_REFLEXPR_BUILD_TESTS ON) + +if (FOX_REFLEXPR_BUILD_SAMPLES) add_subdirectory("sample") endif() -# if (test) -# add_subdirectory("test") -# endif() \ No newline at end of file +if (FOX_REFLEXPR_BUILD_TESTS) + add_subdirectory("test") +endif() \ No newline at end of file diff --git a/CMakeSettings.json b/CMakeSettings.json new file mode 100644 index 0000000..7ae5f66 --- /dev/null +++ b/CMakeSettings.json @@ -0,0 +1,62 @@ +{ + "configurations": [ + { + "name": "x64-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "inheritEnvironments": [ "msvc_x64_x64" ], + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "" + }, + { + "name": "x64-Clang-Release", + "generator": "Ninja", + "configurationType": "RelWithDebInfo", + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "", + "inheritEnvironments": [ "clang_cl_x64_x64" ], + "variables": [] + }, + { + "name": "Mingw64-Release", + "generator": "Ninja", + "configurationType": "RelWithDebInfo", + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "", + "inheritEnvironments": [ "mingw_64" ], + "environments": [ + { + "MINGW64_ROOT": "C:/msys64/mingw64", + "BIN_ROOT": "${env.MINGW64_ROOT}/bin", + "FLAVOR": "x86_64-w64-mingw32", + "TOOLSET_VERSION": "9.1.0", + "PATH": "${env.MINGW64_ROOT}/bin;${env.MINGW64_ROOT}/../usr/local/bin;${env.MINGW64_ROOT}/../usr/bin;${env.MINGW64_ROOT}/../bin;${env.PATH}", + "INCLUDE": "${env.INCLUDE};${env.MINGW64_ROOT}/include/c++/${env.TOOLSET_VERSION};${env.MINGW64_ROOT}/include/c++/${env.TOOLSET_VERSION}/tr1;${env.MINGW64_ROOT}/include/c++/${env.TOOLSET_VERSION}/${env.FLAVOR}", + "environment": "mingw_64" + } + ], + "variables": [ + { + "name": "CMAKE_C_COMPILER", + "value": "${env.BIN_ROOT}/gcc.exe", + "type": "STRING" + }, + { + "name": "CMAKE_CXX_COMPILER", + "value": "${env.BIN_ROOT}/g++.exe", + "type": "STRING" + } + ], + "intelliSenseMode": "linux-gcc-x64" + } + ] +} \ No newline at end of file diff --git a/include/fox/reflexpr.hpp b/include/fox/reflexpr.hpp index 8de7b75..5665e09 100644 --- a/include/fox/reflexpr.hpp +++ b/include/fox/reflexpr.hpp @@ -1,17 +1,14 @@ -/// This header is part of Ruby Ecosystem distributed under MIT license. +/// This header is distributed under MIT license. /// /// Author: Marcin Poloczek (aka. RedSkittleFox) /// Contact: RedSkittleFox@gmail.com /// Copyright: Marcin Poloczek /// License: MIT -/// Version: 2.0.0 +/// Version: 3.0.0 /// -/// TODO: -/// * Improve struct parsing. -/// * Introduce noreflect keyword -/// -#ifndef FOX_REFLEXPR_RUBY_REFLEXPR_H_ -#define FOX_REFLEXPR_RUBY_REFLEXPR_H_ + +#ifndef FOX_REFLEXPR_REFLEXPR_H_ +#define FOX_REFLEXPR_REFLEXPR_H_ #pragma once #include <string> @@ -25,409 +22,342 @@ #include <cassert> #include <functional> #include <type_traits> +#include <tuple> +#include <limits> +#include <utility> namespace fox::reflexpr { - /// <summary> - /// Satisifes std::is_aggregate - /// https://en.cppreference.com/w/cpp/types/is_aggregate - /// </summary> template<class T> concept aggregate = std::is_aggregate_v<std::remove_cvref_t<T>>; - - /// <summary> - /// Iterates over every member variable of an aggregate - /// </summary> - /// <typeparam name="T">Aggregate's Type</typeparam> - /// <typeparam name="Pred">Predicate's Type</typeparam> - /// <param name="obj">Aggregate to iterate over</param> - /// <param name="pred">Predicate that iterates over object. Requires to be invocable with T&, where T is a member type of an aggregate.</param> - /// <example> - /// void foo() - /// { - /// std::cout << "For each member variable:\n"; - /// auto func = []<class T>(T & v) - /// { - /// std::cout << "Type: " << typeid(T).name() << " Value: " << v << '\n'; - /// }; - /// - /// aggregate_type at{ 1 , 3.5f, "Foxes are great!" }; - /// - /// reflexpr::for_each_member_variable(at, func); - /// std::cout << '\n'; - /// } - /// </example> - template<aggregate T, class Pred> - void for_each_member_variable(T&& obj, Pred&& pred); - - /// <summary> - /// Iterates over every member variable's type of an aggregate - /// </summary> - /// <typeparam name="T">Aggregate's Type</typeparam> - /// <typeparam name="Pred">Predicate's Type</typeparam> - /// <param name="pred">Predicate that iterates over object. Invocable as pred.operator()<T>(), - /// where T is a member type of an aggregate.</param> - /// <example> - /// struct functor - /// { - /// template<class T> - /// void operator()() const - /// { - /// std::cout << "Type: " << typeid(T).name() << '\n'; - /// }; - /// }; - /// - /// void foo() - /// { - /// std::cout << "For each member type:\n"; - /// reflexpr::for_each_member_type<aggregate_type, functor>(functor{}); - /// std::cout << '\n'; - /// } - /// </example> - template<std::default_initializable T, class Pred> - void for_each_member_type(Pred&& pred); - - /// <summary> - /// Iterates over every member variable of an aggregate - /// </summary> - /// <typeparam name="T">Reflected Aggregate's Type</typeparam> - /// <typeparam name="Pred">Predicate's Type</typeparam> - /// <param name="obj">Aggregate to iterate over</param> - /// <param name="pred">Predicate that iterates over object. Requires to be invocable with T& and const std::string&, where T is a member type of an aggregate.</param> - /// <example> - /// - /// REFLECT( - /// struct aggregate_type_reflected - /// { - /// int a; - /// float b; - /// std::string str; - /// } - /// ); - /// void foo() - /// { - /// std::cout << "For each member variable reflected:\n"; - /// auto func = []<class T>(T & v, const std::string & name) - /// { - /// std::cout << "Name: " << name << " Type: " << typeid(T).name() << " Value: " << v << '\n'; - /// }; - /// - /// aggregate_type_reflected at{ 1 , 3.5f, "Foxes are great!" }; - /// - /// reflexpr::for_each_reflected_member_variable(at, func); - /// std::cout << '\n'; - /// } - /// </example> - template<aggregate T, class Pred> - void for_each_reflected_member_variable(T& obj, Pred&& pred); - - /// <summary> - /// Iterates over every member variable's type of an aggregate - /// </summary> - /// <typeparam name="T">Reflected Aggregate's Type</typeparam> - /// <typeparam name="Pred">Predicate's Type</typeparam> - /// <param name="pred">Predicate that iterates over object. Invocable as pred.operator()<T>(const std::string&), - /// where T is a member type of an aggregate.</param> - /// <example> - /// REFLECT( - /// struct aggregate_type_reflected - /// { - /// int a; - /// float b; - /// std::string str; - /// } - /// ); - /// - /// struct functor_reflected - /// { - /// template<class T> - /// void operator()(const std::string& name) const - /// { - /// std::cout << "Name: " << name << " Type: " << typeid(T).name() << '\n'; - /// }; - /// }; - /// - /// void foo() - /// { - /// std::cout << "For each member type reflected:\n"; - /// reflexpr::for_each_reflected_member_type<aggregate_type_reflected, functor_reflected>(functor_reflected{}); - /// std::cout << '\n'; - /// } - /// </example> - template<aggregate T, class Pred> - void for_each_reflected_member_type(Pred&& pred); - - /// <summary> - /// Calculates compile time number of member variables - /// </summary> - /// <typeparam name="T">Aggregate Type</typeparam> - template<aggregate T> - struct member_count; } +#define FOX_REFLEXPR_NUM_SUPPORTED_MEMBERS (static_cast<std::size_t>(40)) + namespace fox::reflexpr { - class _reflexpr + namespace details { - struct _member_variable_t + // This is a nasty work-around that makes counting members of aggregates of + // references possible. We iterate over all configurations from N to 0 and check if they + // satisfy aggregate initialization. If we reach zero, we check if type is empty, if not, then that means + // there are more members than selected N. We could transfer initial size and increase it, but + // picking a sane value like 30 should satisfy 90% of cases. + template<size_t I> + struct to_any_type_reference { - std::string name; - std::type_index type_index = std::type_index(typeid(void)); - std::ptrdiff_t offset; + template<class T> + constexpr operator T& (); }; - struct _type_t - { - std::string name; - std::type_index type_index = std::type_index(typeid(void)); - std::vector<_member_variable_t> member_variables; - }; + template<class T, class, size_t... I> + struct member_construct_helper_1 : std::false_type {}; - private: - inline static std::unordered_map<std::type_index, _type_t> type_info_; + template<class T, size_t... I> + struct member_construct_helper_1 < T, std::void_t<decltype( + T{ std::declval<to_any_type_reference<I>>()... } + ) > , I... > : std::true_type {}; - public: - template<aggregate T, class Pred> - static void for_each_member_variable(T& obj, Pred&& pred) - { - auto r = type_info_.find(std::type_index(typeid(T))); - if (r == std::end(type_info_)) - throw std::logic_error(std::string("reflexpr: Trying to use not reflected type: ") + typeid(T).name()); + template<class T, class IndexList> + struct member_counter_helper_2; - const auto& type = r->second; + template<class T, size_t... I> + struct member_counter_helper_2<T, std::index_sequence<I...>> + : member_construct_helper_1<T, void, I...> {}; - auto func = [index = static_cast<size_t>(0llu), &type, &pred]<class U>(U&& v) mutable - { - std::invoke(pred, std::forward<U>(v), type.member_variables[index].name); - ++index; - }; + template<class T, size_t Size> + struct member_counter_helper_3 + : member_counter_helper_2<T, std::make_index_sequence<Size>> {}; - ::fox::reflexpr::for_each_member_variable(obj, func); + template<class T, size_t I> + struct member_counter_helper_4 + { + static constexpr size_t value = + member_counter_helper_3<T, I>::value ? + I : member_counter_helper_4<T, I - 1>::value; }; - private: - inline static size_t counter_ = 0; - template<class T> - static void type_register_member_variable_names_(std::string contents) try + struct member_counter_helper_4<T, 0> { - // get to the inside of the struct - contents = std::string(std::find(std::begin(contents), std::end(contents), '{') + 1, std::end(contents) - 1); - - static const std::string member_var_pattern = - R"(([a-zA-Z_]+\w*(?=\s*;)))"; - - static const std::regex member_var_exp(member_var_pattern, - std::regex::ECMAScript | std::regex::optimize); - - static const std::string comment_pattern = - R"(((?:\/\*)[\w\s]*(?:\*\/))|((?:\/\/)[\w\s]*\n))"; - - static const std::regex comment_exp(comment_pattern, - std::regex::ECMAScript | std::regex::optimize); - - static const std::string remove_initializations_pattern - = R"((\=\s*\{[\w\s,.=\{\}]*\})|(\s*\{[\w\s,.=\{\}]*\})|(=\s*[\w\s,.=\{\}]*))"; - - static const std::regex remove_initializations_exp(remove_initializations_pattern, - std::regex::ECMAScript | std::regex::optimize); - - // Remove comments - contents = std::regex_replace(contents, comment_exp, "\n"); - contents = std::regex_replace(contents, remove_initializations_exp, "\n"); - - // Find member variables - auto match = std::sregex_iterator( - std::begin(contents), std::end(contents), member_var_exp); + static constexpr size_t value = + std::is_empty_v<T> ? + 0 : std::numeric_limits<size_t>::max(); + }; + } - auto& type = type_info_[std::type_index(typeid(T))]; + /** + * \brief Provides access to the number of elements in a tuple as a compile-time constant expression. + * \tparam T Aggregate type + */ + template<aggregate T> + struct tuple_size : + ::fox::reflexpr::details::member_counter_helper_4<std::remove_cvref_t<T>, FOX_REFLEXPR_NUM_SUPPORTED_MEMBERS> {}; - for (auto rng = std::ranges::subrange(match, std::sregex_iterator()); - auto & e : rng) - { - type.member_variables.push_back(_member_variable_t{ e.str() }); - } + /** + * \brief Helper variable template. Provides access to the number of elements in a tuple as a compile-time constant expression. + * \tparam T Aggregate type + */ + template<class T> + static constexpr std::size_t tuple_size_v = tuple_size<T>::value; - // type.member_variables.shrink_to_fit(); - } - catch (const std::exception& e) + namespace details + { + template<std::size_t I, std::size_t J> + struct to_any_type_reference_if_not_j { - assert(false && "ruby_reflexpr: Something went horribly wrong."); + template<class T> + constexpr operator T& (); }; - template<class T> - static void type_register_member_variable_type_infos_() + template<std::size_t I> + struct to_any_type_reference_if_not_j<I, I> { - T temp{}; - auto address = std::bit_cast<ptrdiff_t>(std::addressof(temp)); - auto& type = type_info_[std::type_index(typeid(T))]; + template<class T> + constexpr operator T (); + }; - auto func = [index = static_cast<size_t>(0llu), &type, address]<class U>(U & v) mutable - { - type.member_variables[index].type_index = std::type_index(typeid(U)); - type.member_variables[index].offset = std::bit_cast<ptrdiff_t>(std::addressof(v)) - address; - ++index; - }; + template<class T, class, std::size_t I, std::size_t... Is> + struct is_nth_a_reference_helper_1 : std::false_type {}; - ::fox::reflexpr::for_each_member_variable(temp, func); - } + template<class T, std::size_t I, std::size_t... Is> + struct is_nth_a_reference_helper_1 < T, std::void_t<decltype( + T{ std::declval<to_any_type_reference_if_not_j<I, Is>>()... } + ) > , I, Is... > : std::true_type {}; - public: - template<aggregate T> - static size_t type_register_proxy(std::string contents) - { - if (type_info_.contains(typeid(T))) - return ++counter_; + template<class T, std::size_t I, class> + struct is_nth_a_reference_helper_2; - type_register_member_variable_names_<T>(contents); - type_register_member_variable_type_infos_<T>(); - - return ++counter_; - } - }; + template<class T, std::size_t I, std::size_t... Is> + struct is_nth_a_reference_helper_2<T, I, std::index_sequence<Is...>> : + is_nth_a_reference_helper_1<T, void, I, Is...> {}; - struct _any_type - { - template<class T> - operator T() {} - }; + template<class T, std::size_t I> + struct is_nth_a_reference : is_nth_a_reference_helper_2<T, I, std::make_index_sequence<::fox::reflexpr::tuple_size_v<T>>> {}; - template<aggregate T, class... Args> - struct _member_count - { - constexpr static size_t f(int32_t*) - { - return sizeof...(Args) - 1; - } + template<class T, class> + struct ref_detector_helper_1; - template<class U = T, class Enabled = decltype(U{ Args{}... }) > - constexpr static size_t f(std::nullptr_t) + template<class T, std::size_t... Is> + struct ref_detector_helper_1<T, std::index_sequence<Is...>> { - return _member_count<T, Args..., _any_type>::value; - } - - constexpr static auto value = f(nullptr); - }; + static constexpr std::array<bool, ::fox::reflexpr::tuple_size_v<T>> nth_reference{ std::negation_v<is_nth_a_reference<T, Is>>... }; + }; - template<aggregate T> - struct member_count - { - constexpr static std::size_t value = _member_count<std::remove_cvref_t<T>>::value; - }; + template<class T> + struct ref_detector : ref_detector_helper_1<std::remove_cvref_t<T>, std::make_index_sequence<::fox::reflexpr::tuple_size_v<T>>> {}; + } #ifdef FOX_REFLEXPR_UNPACK_APPLY #pragma message "FOX_REFLEXPR_UNPACK_APPLY macro is internally used by redskittlefox/reflexpr library" #undef FOX_REFLEXPR_UNPACK_APPLY #endif +#ifdef FOX_REFLEXPR_UNPACK_ALL +#pragma message "FOX_REFLEXPR_UNPACK_ALL macro is internally used by redskittlefox/reflexpr library" +#undef FOX_REFLEXPR_UNPACK_ALL +#endif + #define FOX_REFLEXPR_UNPACK_APPLY(SIZE, ...) \ if constexpr ( size == SIZE ) \ { \ auto&& [__VA_ARGS__] = obj; \ - apply_pack( std::in_place_index< SIZE >, __VA_ARGS__ ); \ + return apply_pack( std::in_place_index< SIZE >, __VA_ARGS__ ); \ } - template<aggregate T, class Pred> - void for_each_member_variable(T&& obj, Pred&& pred) +#define FOX_REFLEXPR_UNPACK_ALL \ + FOX_REFLEXPR_UNPACK_APPLY( 1, v0) \ + FOX_REFLEXPR_UNPACK_APPLY( 2, v0, v1) \ + FOX_REFLEXPR_UNPACK_APPLY( 3, v0, v1, v2) \ + FOX_REFLEXPR_UNPACK_APPLY( 4, v0, v1, v2, v3) \ + FOX_REFLEXPR_UNPACK_APPLY( 5, v0, v1, v2, v3, v4) \ + FOX_REFLEXPR_UNPACK_APPLY( 6, v0, v1, v2, v3, v4, v5) \ + FOX_REFLEXPR_UNPACK_APPLY( 7, v0, v1, v2, v3, v4, v5, v6) \ + FOX_REFLEXPR_UNPACK_APPLY( 8, v0, v1, v2, v3, v4, v5, v6, v7) \ + FOX_REFLEXPR_UNPACK_APPLY( 9, v0, v1, v2, v3, v4, v5, v6, v7, v8) \ + FOX_REFLEXPR_UNPACK_APPLY(10, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9) \ + FOX_REFLEXPR_UNPACK_APPLY(11, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) \ + FOX_REFLEXPR_UNPACK_APPLY(12, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) \ + FOX_REFLEXPR_UNPACK_APPLY(13, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) \ + FOX_REFLEXPR_UNPACK_APPLY(14, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) \ + FOX_REFLEXPR_UNPACK_APPLY(15, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) \ + FOX_REFLEXPR_UNPACK_APPLY(16, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \ + FOX_REFLEXPR_UNPACK_APPLY(17, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) \ + FOX_REFLEXPR_UNPACK_APPLY(18, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) \ + FOX_REFLEXPR_UNPACK_APPLY(19, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) \ + FOX_REFLEXPR_UNPACK_APPLY(20, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) \ + FOX_REFLEXPR_UNPACK_APPLY(21, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) \ + FOX_REFLEXPR_UNPACK_APPLY(22, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) \ + FOX_REFLEXPR_UNPACK_APPLY(23, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) \ + FOX_REFLEXPR_UNPACK_APPLY(24, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) \ + FOX_REFLEXPR_UNPACK_APPLY(25, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) \ + FOX_REFLEXPR_UNPACK_APPLY(26, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) \ + FOX_REFLEXPR_UNPACK_APPLY(27, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) \ + FOX_REFLEXPR_UNPACK_APPLY(28, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) \ + FOX_REFLEXPR_UNPACK_APPLY(29, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) \ + FOX_REFLEXPR_UNPACK_APPLY(30, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) \ + FOX_REFLEXPR_UNPACK_APPLY(31, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) \ + FOX_REFLEXPR_UNPACK_APPLY(32, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) \ + FOX_REFLEXPR_UNPACK_APPLY(33, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) \ + FOX_REFLEXPR_UNPACK_APPLY(34, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) \ + FOX_REFLEXPR_UNPACK_APPLY(35, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) \ + FOX_REFLEXPR_UNPACK_APPLY(36, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) \ + FOX_REFLEXPR_UNPACK_APPLY(37, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) \ + FOX_REFLEXPR_UNPACK_APPLY(38, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) \ + FOX_REFLEXPR_UNPACK_APPLY(39, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) \ + FOX_REFLEXPR_UNPACK_APPLY(40, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) + + /** + * \brief Allows iteration over all members of an aggregate type. + * \tparam T Aggregate type + * \tparam Func Function type + * \param obj Object to iterate over members of. + * \param func Functor invoked for each member. + */ + template<aggregate T, class Func> + constexpr void for_each(T&& obj, Func&& func) { - static constexpr size_t size = member_count<T>::value; + constexpr size_t size = tuple_size_v<T>; + static_assert(size <= FOX_REFLEXPR_NUM_SUPPORTED_MEMBERS, "Unsupported number of struct members"); - auto apply_pack = [&]<std::size_t I, class... Args>(std::in_place_index_t<I>, Args&&... args) + auto apply_pack = [&]<std::size_t I, class... Args>(std::in_place_index_t<I>, Args&&... args) -> void { static_assert(I == sizeof...(Args) && "Size and Arguments mismatch."); - auto invoke_proxy = [&]<class U>(U && arg) -> void + if constexpr(std::is_lvalue_reference_v<decltype(obj)> && std::is_const_v<std::remove_reference_t<decltype(obj)>>) { - static_assert(std::is_invocable_v<decltype(pred), decltype(arg)>, "Function is not invokable with type [U]"); - pred( std::forward<U>(arg) ); - }; - - ( invoke_proxy(std::forward<Args>(args) ) , ...); + (func(std::as_const(std::forward<Args>(args))), ...); + } + else + { + (func(std::forward<Args>(args)), ...); + } }; - FOX_REFLEXPR_UNPACK_APPLY(1, v0) - FOX_REFLEXPR_UNPACK_APPLY(2, v0, v1) - FOX_REFLEXPR_UNPACK_APPLY(3, v0, v1, v2) - FOX_REFLEXPR_UNPACK_APPLY(4, v0, v1, v2, v3) - FOX_REFLEXPR_UNPACK_APPLY(5, v0, v1, v2, v3, v4) - FOX_REFLEXPR_UNPACK_APPLY(6, v0, v1, v2, v3, v4, v5) - FOX_REFLEXPR_UNPACK_APPLY(7, v0, v1, v2, v3, v4, v5, v6) - FOX_REFLEXPR_UNPACK_APPLY(8, v0, v1, v2, v3, v4, v5, v6, v7) - FOX_REFLEXPR_UNPACK_APPLY(9, v0, v1, v2, v3, v4, v5, v6, v7, v8) - FOX_REFLEXPR_UNPACK_APPLY(10, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9) - FOX_REFLEXPR_UNPACK_APPLY(11, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) - FOX_REFLEXPR_UNPACK_APPLY(12, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) - FOX_REFLEXPR_UNPACK_APPLY(13, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) - FOX_REFLEXPR_UNPACK_APPLY(14, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) - FOX_REFLEXPR_UNPACK_APPLY(15, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) - FOX_REFLEXPR_UNPACK_APPLY(16, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) - FOX_REFLEXPR_UNPACK_APPLY(17, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) - FOX_REFLEXPR_UNPACK_APPLY(18, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) - FOX_REFLEXPR_UNPACK_APPLY(19, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) - FOX_REFLEXPR_UNPACK_APPLY(20, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) - FOX_REFLEXPR_UNPACK_APPLY(21, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) - FOX_REFLEXPR_UNPACK_APPLY(22, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) - FOX_REFLEXPR_UNPACK_APPLY(23, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) - FOX_REFLEXPR_UNPACK_APPLY(24, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) - FOX_REFLEXPR_UNPACK_APPLY(25, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) - FOX_REFLEXPR_UNPACK_APPLY(26, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) - FOX_REFLEXPR_UNPACK_APPLY(27, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) - FOX_REFLEXPR_UNPACK_APPLY(28, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) - FOX_REFLEXPR_UNPACK_APPLY(29, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) - FOX_REFLEXPR_UNPACK_APPLY(30, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) - FOX_REFLEXPR_UNPACK_APPLY(31, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) - FOX_REFLEXPR_UNPACK_APPLY(32, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) - FOX_REFLEXPR_UNPACK_APPLY(33, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) - FOX_REFLEXPR_UNPACK_APPLY(34, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) - FOX_REFLEXPR_UNPACK_APPLY(35, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) - FOX_REFLEXPR_UNPACK_APPLY(36, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) - FOX_REFLEXPR_UNPACK_APPLY(37, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) - FOX_REFLEXPR_UNPACK_APPLY(38, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) - FOX_REFLEXPR_UNPACK_APPLY(39, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) - FOX_REFLEXPR_UNPACK_APPLY(40, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) - - static_assert(size <= 40, "Unsupported number of struct members"); + FOX_REFLEXPR_UNPACK_ALL }; -#undef FOX_REFLEXPR_UNPACK_APPLY - - template<std::default_initializable T, class Pred> - void for_each_member_type(Pred&& pred) + /** + * \brief Creates a tuple of lvalue references to members of an aggregate. + * \tparam T Aggregate type + * \param obj Object to make a tie of. + * \return A std::tuple object containing lvalue references. + */ + template<aggregate T> + constexpr auto tie(T& obj) { - auto proxy = [&pred]<class U>(const U & v) -> void + constexpr size_t size = tuple_size_v<T>; + static_assert(size <= FOX_REFLEXPR_NUM_SUPPORTED_MEMBERS, "Unsupported number of struct members"); + + auto apply_pack = [&]<std::size_t I, class... Args>(std::in_place_index_t<I>, Args&&... args) { - pred.template operator() < U > (); + static_assert(I == sizeof...(Args) && "Size and Arguments mismatch."); + if constexpr(std::is_const_v<T>) + { + return std::tuple<std::add_lvalue_reference_t<std::add_const_t<std::remove_cvref_t<Args>>>...>{ std::forward<Args>(args)...}; + } + else + { + return std::tuple<Args&...>{ std::forward<Args>(args)...}; + } }; - const T* v = nullptr; - for_each_member_variable(*v, proxy); - }; + FOX_REFLEXPR_UNPACK_ALL + } - template<aggregate T, class Pred> - void for_each_reflected_member_variable(T& obj, Pred&& pred) + /** + * \brief Creates a tuple object, deducing the target type from the types of members. + * \tparam T Aggregate type + * \param obj Object to make the tuple from. + * \return A std::tuple object containing the given values. + */ + template<aggregate T> + constexpr auto make_tuple(T&& obj) { - _reflexpr::for_each_member_variable(obj, pred); - }; + constexpr size_t size = tuple_size_v<T>; + static_assert(size <= FOX_REFLEXPR_NUM_SUPPORTED_MEMBERS, "Unsupported number of struct members"); - template<aggregate T, class Pred> - void for_each_reflected_member_type(Pred&& pred) - { - auto proxy = [&pred]<class U>(const U & v, const std::string & name) -> void + auto apply_pack = [&]<std::size_t I, class... Args>(std::in_place_index_t<I>, Args&&... args) { - pred.template operator() < U > (name); + static_assert(I == sizeof...(Args) && "Size and Arguments mismatch."); + using ref_tester = details::ref_detector<T>; + + return [&]<std::size_t... Is>(std::index_sequence<Is...>) + { + using tuple = std::tuple<Args...>; + + if + constexpr ( std::is_const_v<std::remove_reference_t<T>> ) + { + return std::tuple< + std::conditional_t < ref_tester::nth_reference[Is], + std::add_lvalue_reference_t<std::add_const_t<std::decay_t<std::tuple_element_t<Is, tuple>>>>, + std::remove_reference_t<std::decay_t<std::tuple_element_t<Is, tuple>>> + > ... >(std::forward<Args>(args)...); + } + else + { + return std::tuple< + std::conditional_t < ref_tester::nth_reference[Is], + std::add_lvalue_reference_t<std::decay_t<std::tuple_element_t<Is, tuple>>>, + std::remove_reference_t<std::decay_t<std::tuple_element_t<Is, tuple>>> + > ... >(std::forward<Args>(args)...); + } + }(std::index_sequence_for<Args...>{}); }; - const T* v = nullptr; - _reflexpr::for_each_member_variable(*v, proxy); - }; + FOX_REFLEXPR_UNPACK_ALL + } -} +#undef FOX_REFLEXPR_UNPACK_APPLY + /** + * \brief Provides compile-time indexed access to the types of the elements of the aggregate + * \tparam I Index of the element + * \tparam T Aggregate type + */ + template<std::size_t I, aggregate T> + struct tuple_element + { + using type = std::tuple_element_t<I, decltype(::fox::reflexpr::make_tuple(std::declval<T>()))>; + }; -#define _REFLECT_IDENTIFIER_PROXY(X) reflexpr_##X -#define _REFLECT_IDENTIFIER(X) _REFLECT_IDENTIFIER_PROXY(X) + /** + * \brief Helper variable template. Provides compile-time indexed access to the types of the elements of the aggregate + * \tparam I Index of the element + * \tparam T Aggregate type + */ + template<std::size_t I, aggregate T> + using tuple_element_t = typename tuple_element<I, T>::type; + + /** + * \brief Extracts the Ith element from the aggregate. + * \tparam I Index of the element + * \tparam T Aggregate type + * \param obj Object to extract element from + * \return A reference to selected element of obj + */ + template<std::size_t I, aggregate T> + auto get(T& obj) noexcept -> + std::add_lvalue_reference_t<fox::reflexpr::tuple_element_t<I, T>> + requires ( tuple_size_v<T> > I ) + { + return std::get<I>(fox::reflexpr::tie(obj)); + } -#define REFLECT(...) typedef __VA_ARGS__ _REFLECT_IDENTIFIER(__LINE__)##_t;\ - static size_t _REFLECT_IDENTIFIER(__LINE__)##_proxy = \ - ::fox::reflexpr::_reflexpr::type_register_proxy<_REFLECT_IDENTIFIER(__LINE__)##_t>(#__VA_ARGS__);\ + /** + * \brief Extracts the Ith element from the aggregate. + * \tparam I Index of the element + * \tparam T Aggregate type + * \param obj Object to extract element from + * \return A reference to selected element of obj + */ + template<std::size_t I, aggregate T> + auto get(const T& obj) noexcept -> + std::add_lvalue_reference_t<std::add_const_t<std::remove_cvref_t<fox::reflexpr::tuple_element_t<I, T>>>> + requires (tuple_size_v<T> > I) + { + return std::get<I>(fox::reflexpr::tie(obj)); + } +} #endif \ No newline at end of file diff --git a/sample/CMakeLists.txt b/sample/CMakeLists.txt index d378fb3..4e82384 100644 --- a/sample/CMakeLists.txt +++ b/sample/CMakeLists.txt @@ -15,7 +15,7 @@ if(${IPO_SUPPORTED}) set_target_properties(reflexpr-demo PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) endif() -if(MSVC) +if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") target_compile_options( reflexpr-demo PRIVATE /WX # all warnings as errors diff --git a/sample/main.cpp b/sample/main.cpp index 4bf6e45..0097133 100644 --- a/sample/main.cpp +++ b/sample/main.cpp @@ -2,89 +2,50 @@ #include <string_view> #include <algorithm> #include <string> +#include <array> #include <fox/reflexpr.hpp> -struct aggregate_type +struct my_aggregate { int a; float b; - std::string str; -}; - -REFLECT( -struct aggregate_type_reflected -{ - int a; - float b; - std::string str; -} -); - -struct functor -{ - template<class T> - void operator()() const - { - std::cout << "Type: " << typeid(T).name() << '\n'; - } -}; - -struct functor_reflected -{ - template<class T> - void operator()(const std::string& name) const - { - std::cout << "Name: " << name << " Type: " << typeid(T).name() << '\n'; - } + std::string c; + int& d; }; int main() { - // DEMO: fox::reflexpr::for_each_member_variable + int d = 5; + my_aggregate obj { - std::cout << "For each member variable:\n"; - auto func = []<class T>(T & v) - { - std::cout << "Type: " << typeid(T).name() << " Value: " << v << '\n'; - }; - - aggregate_type at{ 1 , 3.5f, "Foxes are great!" }; + .a = 1 , + .b = 3.5f, + .c = "Foxes are great!", + .d = d + }; - static_assert(fox::reflexpr::aggregate<aggregate_type>, "sus"); - fox::reflexpr::for_each_member_variable(at, func); - std::cout << '\n'; - } - - // DEMO: fox::reflexpr::for_each_member_type - { - std::cout << "For each member type:\n"; + auto&& [v0, v1, v2, v3] = obj; - fox::reflexpr::for_each_member_type<aggregate_type, functor>(functor{}); - std::cout << '\n'; - } + // Get Nth member - fox::reflexpr::get<N>(aggregate) + std::cout << fox::reflexpr::get<0>(obj) << '\n'; // prints obj.a + + // Iterate over members - fox::reflexpr::for_each(aggregate, func) + fox::reflexpr::for_each(obj, [](auto&& v) {std::cout << v << ' '; }), std::cout << '\n'; - // DEMO: fox::reflexpr::for_each_reflected_member_variable - { - std::cout << "For each member variable reflected:\n"; - auto func = []<class T>(T & v, const std::string& name) - { - std::cout << "Name: " << name << " Type: " << typeid(T).name() << " Value: " << v << '\n'; - }; + // Create a tuple-tie from members - fox::reflexpr::tie(aggregate) + auto tie = fox::reflexpr::tie(obj); + std::cout << (std::get<2>(tie) = 2) << '\n'; - aggregate_type_reflected at{ 1 , 3.5f, "Foxes are great!" }; + // Create a tuple from members - fox::reflexpr::make_tuple(aggregate) + auto tuple = fox::reflexpr::make_tuple(obj); + std::cout << (std::get<2>(tuple)) << '\n'; - fox::reflexpr::for_each_reflected_member_variable(at, func); - std::cout << '\n'; - } + // Tuple size - fox::reflexpr::tuple_size_v<aggregate_type> + static_assert(fox::reflexpr::tuple_size_v<my_aggregate> == static_cast<std::size_t>(4)); - // DEMO: fox::reflexpr::for_each_reflected_member_type - { - std::cout << "For each member type reflected:\n"; + // Tuple Nth type + static_assert(std::is_same_v<fox::reflexpr::tuple_element_t<3, my_aggregate>, int&>); - fox::reflexpr::for_each_reflected_member_type<aggregate_type_reflected, functor_reflected>(functor_reflected{}); - std::cout << '\n'; - } - - return 1; + return 0; } \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..1d07bca --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,77 @@ +cmake_minimum_required(VERSION 3.5) + +cmake_policy(PUSH) + +enable_testing() + +if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") + cmake_policy(SET CMP0135 NEW) +endif() + +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/refs/tags/v1.13.0.zip +) +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +set(sources + "${CMAKE_CURRENT_SOURCE_DIR}/reflexpr_test.cc" + "${CMAKE_CURRENT_SOURCE_DIR}/reflexpr_test_generator.py" +) + +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${sources}) + +add_executable( + reflexpr-test + ${sources} +) + +if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") + target_compile_options( + reflexpr-test + PRIVATE /W4 + PRIVATE /MP + PRIVATE /arch:AVX2 + PRIVATE /WX + PRIVATE /bigobj + ) + +endif() + +target_link_libraries( + reflexpr-test + GTest::gtest_main + GTest::gmock_main + reflexpr +) + +find_package(Python COMPONENTS Interpreter Development) +if(NOT ${Python_FOUND}) + message( FATAL_ERROR "Failed to locate python." ) +endif() + +message("${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/reflexpr_test_generator.py") + +add_custom_target( + reflexpr-test-generator + COMMAND ${Python_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/reflexpr_test_generator.py" "${CMAKE_CURRENT_SOURCE_DIR}" + BYPRODUCTS reflexpr_test_types.inl + COMMENT "RedSkittleFox::Reflexpr: Generating test types" +) + +add_dependencies(reflexpr-test reflexpr-test-generator) + +include(GoogleTest) +gtest_discover_tests(reflexpr-test) + +if (PROJECT_IS_TOP_LEVEL) + set_target_properties(gtest_main PROPERTIES FOLDER "vendor") + set_target_properties(gtest PROPERTIES FOLDER "vendor") + set_target_properties(gmock_main PROPERTIES FOLDER "vendor") + set_target_properties(gmock PROPERTIES FOLDER "vendor") +endif() + +cmake_policy(POP) \ No newline at end of file diff --git a/test/reflexpr_test.cc b/test/reflexpr_test.cc new file mode 100644 index 0000000..fb2ae7e --- /dev/null +++ b/test/reflexpr_test.cc @@ -0,0 +1,240 @@ +#include <gtest/gtest.h> +#include <fox/reflexpr.hpp> + +#include <random> +#include <ranges> +#include <concepts> +#include <array> +#include <utility> + +namespace fox::reflexpr +{ +#include "reflexpr_test_types.inl" +} + +namespace fox::reflexpr +{ + template<class Type> + class reflexpr_test : public testing::Test {}; + + TYPED_TEST_SUITE_P(reflexpr_test); + + TYPED_TEST_P(reflexpr_test, aggregate_concept) + { + using value_type = TypeParam; + EXPECT_EQ(std::is_aggregate_v<value_type>, static_cast<bool>(::fox::reflexpr::aggregate<value_type>)); + } + + TYPED_TEST_P(reflexpr_test, for_each) + { + using value_type = TypeParam; + + value_type v; + + auto arr = [&]<std::size_t... Is>(std::index_sequence<Is...>) + { + return std::array<int, value_type::member_count>{ + v.template get<Is>()... + }; + }(std::make_index_sequence<value_type::member_count>{}); + + ::fox::reflexpr::for_each(v, [&, i = static_cast<std::size_t>(0)]<class T>(T&& e) mutable + { + EXPECT_TRUE(std::is_lvalue_reference_v<decltype(e)> && std::negation_v<std::is_const<std::remove_reference_t<decltype(e)>>>); + EXPECT_EQ(arr[i++], e); + }); + + ::fox::reflexpr::for_each(std::as_const(v), [&, i = static_cast<std::size_t>(0)]<class T>(T&& e) mutable + { + if constexpr(std::is_lvalue_reference_v<decltype(e)>) + { + EXPECT_TRUE(std::is_const_v<std::remove_reference_t<decltype(e)>>) + << typeid(T).name(); + } + + EXPECT_EQ(arr[i++], e); + }); + } + + TYPED_TEST_P(reflexpr_test, tuple_size) + { + using value_type = TypeParam; + EXPECT_EQ(value_type::member_count, ::fox::reflexpr::tuple_size<value_type>::value); + EXPECT_EQ(value_type::member_count, ::fox::reflexpr::tuple_size_v<value_type>); + } + + TYPED_TEST_P(reflexpr_test, tuple_element) + { + using value_type = TypeParam; + []<std::size_t... Is>(std::index_sequence<Is...>) + { + EXPECT_TRUE( + (std::is_same_v<decltype(std::declval<value_type>().template get<Is>()), fox::reflexpr::tuple_element_t<Is, value_type>> && ...) + ); + }(std::make_index_sequence<value_type::member_count>{}); + } + + TYPED_TEST_P(reflexpr_test, get) + { + using value_type = TypeParam; + { + value_type value; + + [&] <std::size_t... Is>(std::index_sequence<Is...>) + { + ([&]<std::size_t I>(std::in_place_index_t<I>) + { + auto& v0 = fox::reflexpr::get<I>(value); + auto v1 = value.template get<I>(); + EXPECT_TRUE(std::negation_v<std::is_const<std::remove_reference_t<decltype(v0)>>>); + + EXPECT_EQ(v0, v1); + v0 *= 2; + auto v3 = value.template get<I>(); + EXPECT_NE(v0, v1); + EXPECT_NE(v1, v3); + EXPECT_EQ(v0, v3); + + }(std::in_place_index<Is>), ...); + }(std::make_index_sequence<value_type::member_count>{}); + } + + { + value_type value; + + [&] <std::size_t... Is>(std::index_sequence<Is...>) + { + ([&]<std::size_t I>(std::in_place_index_t<I>) + { + auto& v0 = fox::reflexpr::get<Is>(std::as_const(value)); + auto v1 = value.template get<I>(); + EXPECT_EQ(v0, v1); + EXPECT_TRUE(std::is_const_v<std::remove_reference_t<decltype(v0)>>); + }(std::in_place_index<Is>), ...); + }(std::make_index_sequence<value_type::member_count>{}); + } + } + + TYPED_TEST_P(reflexpr_test, make_tuple) + { + using value_type = TypeParam; + + { + value_type value; + auto tuple = fox::reflexpr::tie(value); + + [&] <std::size_t... Is>(std::index_sequence<Is...>) + { + ([&]<std::size_t I>(std::in_place_index_t<I>) + { + auto& v0 = fox::reflexpr::get<Is>(value); + auto v1 = value.template get<I>(); + EXPECT_TRUE(std::negation_v<std::is_const<std::remove_reference_t<decltype(v0)>>>); + + EXPECT_EQ(v0, v1); + v0 *= 2; + auto v3 = value.template get<I>(); + EXPECT_NE(v0, v1); + EXPECT_NE(v1, v3); + EXPECT_EQ(v0, v3); + }(std::in_place_index<Is>), ...); + }(std::make_index_sequence<value_type::member_count>{}); + } + + { + value_type value; + auto tuple = fox::reflexpr::tie(std::as_const(value)); + + [&] <std::size_t... Is>(std::index_sequence<Is...>) + { + ([&]<std::size_t I>(std::in_place_index_t<I>) + { + auto& v0 = std::get<I>(tuple); + auto v1 = value.template get<I>(); + EXPECT_EQ(v0, v1); + EXPECT_TRUE(std::is_const_v<std::remove_reference_t<decltype(v0)>>) + << typeid(tuple).name(); + }(std::in_place_index<Is>), ...); + }(std::make_index_sequence<value_type::member_count>{}); + } + } + + TYPED_TEST_P(reflexpr_test, tie) + { + using value_type = TypeParam; + + using value_type = TypeParam; + + { + value_type value; + auto tuple = fox::reflexpr::make_tuple(value); + using tuple_type = std::remove_cvref_t<decltype(tuple)>; + [&] <std::size_t... Is>(std::index_sequence<Is...>) + { + ([&]<std::size_t I>(std::in_place_index_t<I>) + { + auto& v0 = std::get<Is>(tuple); + auto v1 = value.template get<I>(); + EXPECT_TRUE(std::negation_v<std::is_const<std::remove_reference_t<decltype(v0)>>>); + + if + constexpr ( + std::is_same_v< + std::tuple_element_t<I, tuple_type>, + decltype(std::declval<value_type>().template get<I>()) + > && + std::is_lvalue_reference_v<std::tuple_element_t<I, tuple_type>> + ) + { + EXPECT_EQ(v0, v1); + v0 *= 2; + auto v3 = value.template get<I>(); + EXPECT_NE(v0, v1); + EXPECT_NE(v1, v3); + EXPECT_EQ(v0, v3); + } + }(std::in_place_index<Is>), ...); + }(std::make_index_sequence<value_type::member_count>{}); + } + + { + value_type value; + auto tuple = fox::reflexpr::make_tuple(std::as_const(value)); + using tuple_type = std::remove_reference_t<decltype(tuple)>; + [&] <std::size_t... Is>(std::index_sequence<Is...>) + { + ([&]<std::size_t I>(std::in_place_index_t<I>) + { + auto& v0 = std::get<I>(tuple); + auto v1 = value.template get<I>(); + EXPECT_EQ(v0, v1); + if constexpr (std::is_lvalue_reference_v<std::tuple_element_t<I, tuple_type>>) + { + EXPECT_TRUE(std::is_const_v<std::remove_reference_t<decltype(v0)>>); + } + }(std::in_place_index<Is>), ...); + }(std::make_index_sequence<value_type::member_count>{}); + } + } + + struct test_aggregate_test + { + static constexpr std::size_t member_count = 2; + + template<std::size_t I> + auto get() -> decltype(auto) requires (I < member_count) + { + if constexpr (I == 0) + return a; + if constexpr (I == 1) + return b; + } + + static inline int s_a = 1; + int& a = s_a; + int b = 2; + }; + + REGISTER_TYPED_TEST_SUITE_P(reflexpr_test, aggregate_concept, for_each, tuple_size, tuple_element, get, make_tuple, tie); + INSTANTIATE_TYPED_TEST_SUITE_P(fundamental, reflexpr_test, types); +} \ No newline at end of file diff --git a/test/reflexpr_test_generator.py b/test/reflexpr_test_generator.py new file mode 100644 index 0000000..c203c78 --- /dev/null +++ b/test/reflexpr_test_generator.py @@ -0,0 +1,56 @@ +import os +import sys + +num_supported_members : int = 40 +out = "" + +for i in range(1, num_supported_members): + out_class = """ +struct test_aggregate_%d +{ + static constexpr std::size_t member_count = %d; + + template<std::size_t I> + auto get() -> decltype(auto) requires (I < member_count) + { +""" % (i, i) + + for j in range(1, i + 1): + out_class = out_class + """ + if constexpr (I == %d) + return v%d; +""" % (j - 1, j) + + out_class = out_class + """ + } + """ + + for j in range(1, i + 1): + if j % 2 == 0: + out_class = out_class + """ + int v%d = %d;""" % (j, j) + else: + out_class = out_class + """ + static inline int s_v%d = %d; + int& v%d = s_v%d;""" % (j, j, j, j) + + out_class = out_class + """ +}; +""" + out = out + out_class + +out = out + f""" +using types = testing::Types< + """ + +for i in range(1, num_supported_members): + out = out + f"""test_aggregate_{i}""" + if i + 1 != num_supported_members: + out += ", " + +out = out + """>;""" + + +f = open(f"{sys.argv[1]}/reflexpr_test_types.inl", "w") +f.write(out) +f.close() \ No newline at end of file From 89c9404c41bceedbe50c33509fb82b3d4c5a2119 Mon Sep 17 00:00:00 2001 From: RedSkittleFox <RedSkittleFox@gmail.com> Date: Wed, 17 Jan 2024 15:54:02 +0100 Subject: [PATCH 2/7] workflow and readme update --- .github/workflows/cmake-msvc-build.yml | 39 -------- .github/workflows/cmake-multi-platform.yml | 75 +++++++++++++++ README.md | 102 ++++++--------------- sample/main.cpp | 4 - 4 files changed, 102 insertions(+), 118 deletions(-) delete mode 100644 .github/workflows/cmake-msvc-build.yml create mode 100644 .github/workflows/cmake-multi-platform.yml diff --git a/.github/workflows/cmake-msvc-build.yml b/.github/workflows/cmake-msvc-build.yml deleted file mode 100644 index 1704bd8..0000000 --- a/.github/workflows/cmake-msvc-build.yml +++ /dev/null @@ -1,39 +0,0 @@ -# This starter workflow is for a CMake project running on a single platform. There is a different starter workflow if you need cross-platform coverage. -# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-multi-platform.yml -name: CMake MSVC Build and Test - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -env: - # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) - BUILD_TYPE: Release - -jobs: - build: - # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. - # You can convert this to a matrix build if you need cross-platform coverage. - # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix - runs-on: windows-latest - - steps: - - uses: actions/checkout@v3 - - - name: Configure CMake - # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. - # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type - run: cmake -Dsample=ON -Dtest=ON -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} - - - name: Build - # Build your program with the given configuration - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} - - # Currently no tests - # - name: Test - # working-directory: ${{github.workspace}}/build - # Execute tests defined by the CMake configuration. - # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - # run: ctest -C ${{env.BUILD_TYPE}} diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml new file mode 100644 index 0000000..e995f98 --- /dev/null +++ b/.github/workflows/cmake-multi-platform.yml @@ -0,0 +1,75 @@ +# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform. +# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml +name: CMake on multiple platforms + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ${{ matrix.os }} + + strategy: + # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable. + fail-fast: false + + # Set up a matrix to run the following 3 configurations: + # 1. <Windows, Release, latest MSVC compiler toolchain on the default runner image, default generator> + # 2. <Linux, Release, latest GCC compiler toolchain on the default runner image, default generator> + # 3. <Linux, Release, latest Clang compiler toolchain on the default runner image, default generator> + # + # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list. + matrix: + os: [ubuntu-latest, windows-latest] + build_type: [Release] + c_compiler: [gcc, clang, cl] + include: + - os: windows-latest + c_compiler: cl + cpp_compiler: cl + - os: ubuntu-latest + c_compiler: gcc + cpp_compiler: g++ + - os: ubuntu-latest + c_compiler: clang + cpp_compiler: clang++ + exclude: + - os: windows-latest + c_compiler: gcc + - os: windows-latest + c_compiler: clang + - os: ubuntu-latest + c_compiler: cl + + steps: + - uses: actions/checkout@v3 + + - name: Set reusable strings + # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. + id: strings + shell: bash + run: | + echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: > + cmake -B ${{ steps.strings.outputs.build-output-dir }} + -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} + -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + -S ${{ github.workspace }} + + - name: Build + # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} + + - name: Test + working-directory: ${{ steps.strings.outputs.build-output-dir }} + # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest --build-config ${{ matrix.build_type }} diff --git a/README.md b/README.md index e97c18d..c06bd03 100644 --- a/README.md +++ b/README.md @@ -5,102 +5,54 @@ is a c++20 compile and runtime aggregate reflections header only library. It all # Example Usage ```cpp -// demo.cpp #include <iostream> -#include <string_view> -#include <algorithm> #include <string> - #include <fox/reflexpr.hpp> -struct aggregate_type -{ - int a; - float b; - std::string str; -}; - -REFLECT( -struct aggregate_type_reflected +struct my_aggregate { int a; float b; - std::string str; -} -); - -struct functor -{ - template<class T> - void operator()() const - { - std::cout << "Type: " << typeid(T).name() << '\n'; - } -}; - -struct functor_reflected -{ - template<class T> - void operator()(const std::string& name) const - { - std::cout << "Name: " << name << " Type: " << typeid(T).name() << '\n'; - } + std::string c; + int& d; }; int main() { - // DEMO: fox::reflexpr::for_each_member_variable + int d = 5; + my_aggregate obj { - std::cout << "For each member variable:\n"; - auto func = []<class T>(T & v) - { - std::cout << "Type: " << typeid(T).name() << " Value: " << v << '\n'; - }; + .a = 1 , + .b = 3.5f, + .c = "Foxes are great!", + .d = d + }; - aggregate_type at{ 1 , 3.5f, "Foxes are great!" }; - - static_assert(fox::reflexpr::aggregate<aggregate_type>, "sus"); - fox::reflexpr::for_each_member_variable(at, func); - std::cout << '\n'; - } - - // DEMO: fox::reflexpr::for_each_member_type - { - std::cout << "For each member type:\n"; + auto&& [v0, v1, v2, v3] = obj; - fox::reflexpr::for_each_member_type<aggregate_type, functor>(functor{}); - std::cout << '\n'; - } + // Get Nth member - fox::reflexpr::get<N>(aggregate) + std::cout << fox::reflexpr::get<0>(obj) << '\n'; // prints obj.a + + // Iterate over members - fox::reflexpr::for_each(aggregate, func) + fox::reflexpr::for_each(obj, [](auto&& v) {std::cout << v << ' '; }), std::cout << '\n'; - // DEMO: fox::reflexpr::for_each_reflected_member_variable - { - std::cout << "For each member variable reflected:\n"; - auto func = []<class T>(T & v, const std::string& name) - { - std::cout << "Name: " << name << " Type: " << typeid(T).name() << " Value: " << v << '\n'; - }; + // Create a tuple-tie from members - fox::reflexpr::tie(aggregate) + auto tie = fox::reflexpr::tie(obj); + std::cout << (std::get<2>(tie) = 2) << '\n'; - aggregate_type_reflected at{ 1 , 3.5f, "Foxes are great!" }; + // Create a tuple from members - fox::reflexpr::make_tuple(aggregate) + auto tuple = fox::reflexpr::make_tuple(obj); + std::cout << (std::get<2>(tuple)) << '\n'; - fox::reflexpr::for_each_reflected_member_variable(at, func); - std::cout << '\n'; - } + // Tuple size - fox::reflexpr::tuple_size_v<aggregate_type> + static_assert(fox::reflexpr::tuple_size_v<my_aggregate> == static_cast<std::size_t>(4)); - // DEMO: fox::reflexpr::for_each_reflected_member_type - { - std::cout << "For each member type reflected:\n"; + // Tuple Nth type + static_assert(std::is_same_v<fox::reflexpr::tuple_element_t<3, my_aggregate>, int&>); - fox::reflexpr::for_each_reflected_member_type<aggregate_type_reflected, functor_reflected>(functor_reflected{}); - std::cout << '\n'; - } - - return 1; + return 0; } ``` -# Planned Improvements -* Improving member variable names parsing. -* Write unit tests. - # Limitation Right now it supports only up to 40 member variables and introduces small runtime overhead when registering member variable names. diff --git a/sample/main.cpp b/sample/main.cpp index 0097133..0360d60 100644 --- a/sample/main.cpp +++ b/sample/main.cpp @@ -1,9 +1,5 @@ #include <iostream> -#include <string_view> -#include <algorithm> #include <string> -#include <array> - #include <fox/reflexpr.hpp> struct my_aggregate From e1453baeff296dfd7416a643149bd509a4353bc5 Mon Sep 17 00:00:00 2001 From: RedSkittleFox <RedSkittleFox@gmail.com> Date: Wed, 17 Jan 2024 16:03:08 +0100 Subject: [PATCH 3/7] fixed cmake for msvc --- include/fox/reflexpr.hpp | 1 + test/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/fox/reflexpr.hpp b/include/fox/reflexpr.hpp index 5665e09..da3541a 100644 --- a/include/fox/reflexpr.hpp +++ b/include/fox/reflexpr.hpp @@ -11,6 +11,7 @@ #define FOX_REFLEXPR_REFLEXPR_H_ #pragma once +#include <array> #include <string> #include <vector> #include <unordered_map> diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1d07bca..761dd64 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -29,7 +29,7 @@ add_executable( ${sources} ) -if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") target_compile_options( reflexpr-test PRIVATE /W4 From e681bf8dbcc5b5ba7d5d802b1cc35d2b8ebb11c6 Mon Sep 17 00:00:00 2001 From: RedSkittleFox <RedSkittleFox@gmail.com> Date: Wed, 17 Jan 2024 16:06:17 +0100 Subject: [PATCH 4/7] changed cmake to c++20 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 354697c..25607a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ if (PROJECT_IS_TOP_LEVEL) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin-etc") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin") - set(CMAKE_CXX_STANDARD 23) + set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED) endif() From f334e79dccaa5931d4a4582c03a1b3b3885f44fc Mon Sep 17 00:00:00 2001 From: RedSkittleFox <RedSkittleFox@gmail.com> Date: Wed, 17 Jan 2024 16:12:16 +0100 Subject: [PATCH 5/7] disable clang build, actions fail with default clang and C++20 --- .github/workflows/cmake-multi-platform.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index e995f98..07f3415 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -33,14 +33,14 @@ jobs: - os: ubuntu-latest c_compiler: gcc cpp_compiler: g++ - - os: ubuntu-latest - c_compiler: clang - cpp_compiler: clang++ +# - os: ubuntu-latest + # c_compiler: clang + # cpp_compiler: clang++ exclude: - os: windows-latest c_compiler: gcc - - os: windows-latest - c_compiler: clang + # - os: windows-latest +# c_compiler: clang - os: ubuntu-latest c_compiler: cl From 73c99ad65567043cdafa91fee96669c8ebc55a8d Mon Sep 17 00:00:00 2001 From: RedSkittleFox <RedSkittleFox@gmail.com> Date: Wed, 17 Jan 2024 16:13:49 +0100 Subject: [PATCH 6/7] fixed typo --- .github/workflows/cmake-multi-platform.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 07f3415..2a45256 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -25,7 +25,7 @@ jobs: matrix: os: [ubuntu-latest, windows-latest] build_type: [Release] - c_compiler: [gcc, clang, cl] + c_compiler: [gcc, cl] # clang, include: - os: windows-latest c_compiler: cl From 88096a43593a2baa8c2e2098ce7a9cb9b8d82798 Mon Sep 17 00:00:00 2001 From: RedSkittleFox <RedSkittleFox@gmail.com> Date: Wed, 17 Jan 2024 16:15:03 +0100 Subject: [PATCH 7/7] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c06bd03..b911674 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![CMake MSVC Build and Test](https://github.com/RedSkittleFox/reflexpr/actions/workflows/cmake-msvc-build.yml/badge.svg)](https://github.com/RedSkittleFox/reflexpr/actions/workflows/cmake-msvc-build.yml) +[![CMake on multiple platforms](https://github.com/RedSkittleFox/reflexpr/actions/workflows/cmake-multi-platform.yml/badge.svg)](https://github.com/RedSkittleFox/reflexpr/actions/workflows/cmake-multi-platform.yml) # reflexpr is a c++20 compile and runtime aggregate reflections header only library. It allows you to iterate over aggregate type's member variables.