diff --git a/ProcessLib/Reflection/ReflectionData.h b/ProcessLib/Reflection/ReflectionData.h index fd26fd50d2c..97fd57e1ff1 100644 --- a/ProcessLib/Reflection/ReflectionData.h +++ b/ProcessLib/Reflection/ReflectionData.h @@ -15,30 +15,82 @@ namespace ProcessLib::Reflection { -template +/** + * Provides access to a single member of \c Class via an accessor function and + * provides a name that can be used, e.g. to identify that member during I/O + * operations. + * + * \c Accessor is a function taking an instance of \c Class and returning a + * reference to the member. + */ +template struct ReflectionData { - ReflectionData(std::string name, Member Class::*field) - : name(std::move(name)), field(field) + static_assert(std::is_same_v>); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert( + std::is_lvalue_reference_v>); + static_assert(std::is_lvalue_reference_v< + std::invoke_result_t>); + + ReflectionData(std::string name, Accessor&& accessor) + : name(std::move(name)), accessor(std::move(accessor)) { } - explicit ReflectionData(Member Class::*field) : field(field) {} + explicit ReflectionData(Accessor&& accessor) : accessor(std::move(accessor)) + { + } std::string name; - Member Class::*field; + Accessor accessor; }; +template +auto makeReflectionData(Accessor&& accessor) +{ + return ReflectionData>{ + std::forward(accessor)}; +} + +template +auto makeReflectionData(std::string name, Accessor&& accessor) +{ + return ReflectionData>{ + std::move(name), std::forward(accessor)}; +} + +template +auto makeReflectionData(Member Class::*member) +{ + return makeReflectionData([member](auto& obj) -> auto& + { return obj.*member; }); +} + +template +auto makeReflectionData(std::string name, Member Class::*member) +{ + return makeReflectionData( + std::move(name), [member](auto& obj) -> auto& { return obj.*member; }); +} + template -std::tuple> reflectWithName( - std::string const& name, Member Class::*field) +auto reflectWithName(std::string name, Member Class::*member) +{ + return std::tuple{makeReflectionData(std::move(name), member)}; +} + +template +auto reflectWithoutName(Accessors&&... accessors) { - return {{name, field}}; + return std::tuple{ + makeReflectionData(std::forward(accessors))...}; } template auto reflectWithoutName(Members Class::*... members) { - return std::tuple{ReflectionData{"", members}...}; + return std::tuple{makeReflectionData(members)...}; } } // namespace ProcessLib::Reflection diff --git a/ProcessLib/Reflection/ReflectionIPData.h b/ProcessLib/Reflection/ReflectionIPData.h index 9fdc4b66fe0..0353f9b891e 100644 --- a/ProcessLib/Reflection/ReflectionIPData.h +++ b/ProcessLib/Reflection/ReflectionIPData.h @@ -19,11 +19,55 @@ namespace ProcessLib::Reflection { namespace detail { -// Used in metaprogramming to check if the type T has a member "reflect". template -constexpr bool has_reflect = requires +concept has_reflect = requires { T::reflect(); }; + +template +auto reflect(std::type_identity>) +{ + using namespace boost::mp11; + + // The types Ts... must be unique. Duplicate types are incompatible with the + // concept of "reflected" I/O: they would lead to duplicate names for the + // I/O data. + static_assert(mp_is_set>::value); + + return reflectWithoutName>( + [](auto& tuple_) -> auto& { return std::get(tuple_); }...); +} + +template +auto reflect(std::type_identity) +{ + return T::reflect(); +} + +template +concept is_reflectable = requires { + ProcessLib::Reflection::detail::reflect(std::type_identity{}); +}; + +/** + * Raw data is data that will be read or written, e.g., double values or Eigen + * vectors. + * + * Non-raw data is data for which further reflection will be performed (to find + * out the members). + */ +template +struct is_raw_data : std::false_type +{ +}; + +template <> +struct is_raw_data : std::true_type +{ +}; + +template +struct is_raw_data> + : std::true_type { - T::reflect(); }; template @@ -118,6 +162,9 @@ struct GetFlattenedIPDataFromLocAsm using ConcreteIPData = std::remove_cvref_t< std::invoke_result_t>; + static_assert(is_raw_data::value, + "This method only deals with raw data. The given " + "ConcreteIPData is not raw data."); constexpr unsigned num_comp = NumberOfComponents::value; auto const& ip_data_vector = accessor_ip_data_vec_in_loc_asm(loc_asm); @@ -134,7 +181,6 @@ struct GetFlattenedIPDataFromLocAsm if constexpr (num_comp == 1) { - // TODO optimization if nothing needs to be copied? result[ip] = ip_data; } else if constexpr (num_comp == @@ -216,28 +262,35 @@ void forEachReflectedFlattenedIPDataAccessor( reflection_data, [&accessor_ip_data_vec_in_loc_asm, &accessor_current_level_from_ip_data_vec_element, - &callback]( - ReflectionData const& refl_data) + &callback]( + ReflectionData const& refl_data) { + using MemberRef = std::invoke_result_t; + using Member = std::remove_cvref_t; + auto accessor_member_from_ip_data_vec_element = - [level = refl_data.field, + [accessor_next_level = refl_data.accessor, accessor_current_level_from_ip_data_vec_element]( auto const& ip_data_vec_element) -> Member const& { - return accessor_current_level_from_ip_data_vec_element( - ip_data_vec_element).* - level; + return accessor_next_level( + accessor_current_level_from_ip_data_vec_element( + ip_data_vec_element)); }; - if constexpr (has_reflect) + if constexpr (is_reflectable) { forEachReflectedFlattenedIPDataAccessor( - callback, Member::reflect(), + callback, detail::reflect(std::type_identity{}), accessor_ip_data_vec_in_loc_asm, accessor_member_from_ip_data_vec_element); } else { + static_assert(is_raw_data::value, + "The current member is not reflectable, so we " + "expect it to be raw data."); + constexpr unsigned num_comp = NumberOfComponents::value; callback(refl_data.name, num_comp, @@ -280,24 +333,50 @@ void forEachReflectedFlattenedIPDataAccessor(ReflData const& reflection_data, { boost::mp11::tuple_for_each( reflection_data, - [&callback]( - ReflectionData> const& refl_data) + [&callback]( + ReflectionData const& refl_data) { - auto accessor_ip_data_vec_in_loc_asm = [ip_data_vector = - refl_data.field]( - LocAsmIF const& loc_asm) -> auto const& - { - return loc_asm.*ip_data_vector; - }; + static_assert(std::is_same_v, + "The currently processed reflection data is not for " + "the given LocAsmIF but for a different class."); - if constexpr (detail::has_reflect) + using AccessorResultRef = + std::invoke_result_t; + using AccessorResult = std::remove_cvref_t; + + // AccessorResult must be a std::vector. We + // check that, now. + static_assert(boost::mp11::mp_is_list:: + value); // std::vector + // is a list in the Boost MP11 sense + static_assert( + std::is_same_v< + AccessorResult, + boost::mp11::mp_rename>, + "We expect a std::vector, here."); + // Now, we know that AccessorResult is std::vector. To be + // more specific, AccessorResult is a std::vector and Member + // is IPData. + using Member = typename AccessorResult::value_type; + + auto accessor_ip_data_vec_in_loc_asm = + [ip_data_vector_accessor = + refl_data.accessor](LocAsmIF const& loc_asm) -> auto const& + { return ip_data_vector_accessor(loc_asm); }; + + if constexpr (detail::is_reflectable) { detail::forEachReflectedFlattenedIPDataAccessor( - callback, Member::reflect(), + callback, + detail::reflect(std::type_identity{}), accessor_ip_data_vec_in_loc_asm); } else { + static_assert(detail::is_raw_data::value, + "The current member is not reflectable, so we " + "expect it to be raw data."); + constexpr unsigned num_comp = detail::NumberOfComponents::value; diff --git a/ProcessLib/Reflection/ReflectionSetIPData.h b/ProcessLib/Reflection/ReflectionSetIPData.h index 69a15e97d62..b158ab89ce7 100644 --- a/ProcessLib/Reflection/ReflectionSetIPData.h +++ b/ProcessLib/Reflection/ReflectionSetIPData.h @@ -27,6 +27,11 @@ void setIPData(double const* values, { using AccessorResult = std::invoke_result_t; using AccessorResultStripped = std::remove_cvref_t; + + static_assert(is_raw_data::value, + "This method only deals with raw data. The given " + "AccessorResultStripped is not raw data."); + constexpr auto num_comp = NumberOfComponents::value; auto constexpr kv_size = @@ -66,26 +71,33 @@ void setIPData(double const* values, // Sets IP data if the passed name equals the one in refl_data. // Returns true if IP have been set, false otherwise. template + typename Class, typename Accessor> bool setIPDataIfNameMatches(std::string const& name, double const* values, std::vector& ip_data_vector, Accessor_CurrentLevelFromIPData const& accessor, - ReflectionData const& refl_data) + ReflectionData const& refl_data) { - auto const field = refl_data.field; + auto const accessor_next_level = refl_data.accessor; - auto const accessor_field_from_ip_data = [accessor, - field](IPData& ip_data) -> Member& - { return accessor(ip_data).*field; }; + using MemberRef = std::invoke_result_t; + using Member = std::remove_cvref_t; - if constexpr (detail::has_reflect) + auto const accessor_field_from_ip_data = + [accessor, accessor_next_level](IPData& ip_data) -> Member& + { return accessor_next_level(accessor(ip_data)); }; + + if constexpr (detail::is_reflectable) { - return reflectSetIPData(name, values, ip_data_vector, - accessor_field_from_ip_data, - Member::reflect()); + return reflectSetIPData( + name, values, ip_data_vector, accessor_field_from_ip_data, + detail::reflect(std::type_identity{})); } else { + static_assert(detail::is_raw_data::value, + "The current member is not reflectable, so we " + "expect it to be raw data."); + if (refl_data.name != name) { return false; @@ -98,12 +110,12 @@ bool setIPDataIfNameMatches(std::string const& name, double const* values, } template + typename... Classes, typename... Accessors, std::size_t... Idcs> bool reflectSetIPData( std::string const& name, double const* values, std::vector& ip_data_vector, Accessor_CurrentLevelFromIPData const& accessor, - std::tuple...> const& refl_data, + std::tuple...> const& refl_data, std::index_sequence) { // uses short-circuit evaluation of the fold || ... to stop after the first @@ -114,16 +126,16 @@ bool reflectSetIPData( } template + typename... Classes, typename... Accessors> bool reflectSetIPData( std::string const& name, double const* values, std::vector& ip_data_vector, Accessor_CurrentLevelFromIPData const& accessor, - std::tuple...> const& refl_data) + std::tuple...> const& refl_data) { - return reflectSetIPData(name, values, ip_data_vector, accessor, - refl_data, - std::make_index_sequence{}); + return reflectSetIPData( + name, values, ip_data_vector, accessor, refl_data, + std::make_index_sequence{}); } } // namespace detail @@ -140,8 +152,9 @@ template std::size_t reflectSetIPData(std::string const& name, double const* values, std::vector& ip_data_vector) { - detail::reflectSetIPData(name, values, ip_data_vector, std::identity{}, - IPData::reflect()); + detail::reflectSetIPData( + name, values, ip_data_vector, std::identity{}, + detail::reflect(std::type_identity{})); return ip_data_vector.size(); } diff --git a/Tests/ProcessLib/TestReflectIPData.cpp b/Tests/ProcessLib/TestReflectIPData.cpp index 9f59f879008..3169d1572c8 100644 --- a/Tests/ProcessLib/TestReflectIPData.cpp +++ b/Tests/ProcessLib/TestReflectIPData.cpp @@ -27,9 +27,9 @@ struct Level3 static auto reflect() { using namespace ProcessLib::Reflection; - return std::tuple{ReflectionData{"kelvin3", &Level3::kelvin3}, - ReflectionData{"vector3", &Level3::vector3}, - ReflectionData{"scalar3", &Level3::scalar3}}; + return std::tuple{makeReflectionData("kelvin3", &Level3::kelvin3), + makeReflectionData("vector3", &Level3::vector3), + makeReflectionData("scalar3", &Level3::scalar3)}; } }; @@ -41,23 +41,12 @@ struct Level3b static auto reflect() { using namespace ProcessLib::Reflection; - return std::tuple{ReflectionData{"scalar3b", &Level3b::scalar3b}}; + return std::tuple{makeReflectionData("scalar3b", &Level3b::scalar3b)}; } }; template -struct Level2 -{ - Level3 level3; - Level3b level3b; - - static auto reflect() - { - using namespace ProcessLib::Reflection; - return std::tuple{ReflectionData{&Level2::level3}, - ReflectionData{&Level2::level3b}}; - } -}; +using Level2 = std::tuple, Level3b>; template struct Level2b @@ -67,7 +56,7 @@ struct Level2b static auto reflect() { using namespace ProcessLib::Reflection; - return std::tuple{ReflectionData{"scalar2b", &Level2b::scalar2b}}; + return std::tuple{makeReflectionData("scalar2b", &Level2b::scalar2b)}; } }; @@ -83,11 +72,11 @@ struct Level1 static auto reflect() { using namespace ProcessLib::Reflection; - return std::tuple{ReflectionData{"kelvin1", &Level1::kelvin1}, - ReflectionData{"vector1", &Level1::vector1}, - ReflectionData{"scalar1", &Level1::scalar1}, - ReflectionData{&Level1::level2}, - ReflectionData{&Level1::level2b}}; + return std::tuple{makeReflectionData("kelvin1", &Level1::kelvin1), + makeReflectionData("vector1", &Level1::vector1), + makeReflectionData("scalar1", &Level1::scalar1), + makeReflectionData(&Level1::level2), + makeReflectionData(&Level1::level2b)}; } }; @@ -99,7 +88,7 @@ struct Level1b static auto reflect() { using namespace ProcessLib::Reflection; - return std::tuple{ReflectionData{"scalar1b", &Level1b::scalar1b}}; + return std::tuple{makeReflectionData("scalar1b", &Level1b::scalar1b)}; } }; @@ -126,11 +115,12 @@ struct LocAsmIF static auto reflect() { using namespace ProcessLib::Reflection; - return std::tuple{ReflectionData{"scalar", &LocAsmIF::ip_data_scalar}, - ReflectionData{"vector", &LocAsmIF::ip_data_vector}, - ReflectionData{"kelvin", &LocAsmIF::ip_data_kelvin}, - ReflectionData{&LocAsmIF::ip_data_level1}, - ReflectionData{&LocAsmIF::ip_data_level1b}}; + return std::tuple{ + makeReflectionData("scalar", &LocAsmIF::ip_data_scalar), + makeReflectionData("vector", &LocAsmIF::ip_data_vector), + makeReflectionData("kelvin", &LocAsmIF::ip_data_kelvin), + makeReflectionData(&LocAsmIF::ip_data_level1), + makeReflectionData(&LocAsmIF::ip_data_level1b)}; } }; @@ -355,21 +345,24 @@ struct ReferenceData ref.scalar3 = initScalar( loc_asm, start_value(), [](auto& loc_asm, unsigned const ip) -> auto& { - return loc_asm.ip_data_level1[ip].level2.level3.scalar3; + return std::get>(loc_asm.ip_data_level1[ip].level2) + .scalar3; }, for_read_test); ref.vector3 = initVector( loc_asm, start_value(), [](auto& loc_asm, unsigned const ip) -> auto& { - return loc_asm.ip_data_level1[ip].level2.level3.vector3; + return std::get>(loc_asm.ip_data_level1[ip].level2) + .vector3; }, for_read_test); ref.kelvin3 = initKelvin( loc_asm, start_value(), [](auto& loc_asm, unsigned const ip) -> auto& { - return loc_asm.ip_data_level1[ip].level2.level3.kelvin3; + return std::get>(loc_asm.ip_data_level1[ip].level2) + .kelvin3; }, for_read_test); @@ -394,7 +387,8 @@ struct ReferenceData ref.scalar3b = initScalar( loc_asm, start_value(), [](auto& loc_asm, unsigned const ip) -> auto& { - return loc_asm.ip_data_level1[ip].level2.level3b.scalar3b; + return std::get>(loc_asm.ip_data_level1[ip].level2) + .scalar3b; }, for_read_test);