Skip to content

Commit

Permalink
Merge branch 'reflect-io-for-tuples' into 'master'
Browse files Browse the repository at this point in the history
Reflected I/O for std::tuple

See merge request ogs/ogs!4761
  • Loading branch information
endJunction committed Nov 1, 2023
2 parents 6783ef3 + 84da429 commit 980e84e
Show file tree
Hide file tree
Showing 4 changed files with 220 additions and 82 deletions.
70 changes: 61 additions & 9 deletions ProcessLib/Reflection/ReflectionData.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,82 @@

namespace ProcessLib::Reflection
{
template <typename Class, typename Member>
/**
* 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 <typename Class, typename Accessor>
struct ReflectionData
{
ReflectionData(std::string name, Member Class::*field)
: name(std::move(name)), field(field)
static_assert(std::is_same_v<Accessor, std::remove_cvref_t<Accessor>>);
static_assert(std::is_invocable_v<Accessor, Class&>);
static_assert(std::is_invocable_v<Accessor, Class const&>);
static_assert(
std::is_lvalue_reference_v<std::invoke_result_t<Accessor, Class&>>);
static_assert(std::is_lvalue_reference_v<
std::invoke_result_t<Accessor, Class const&>>);

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 <typename Class, typename Accessor>
auto makeReflectionData(Accessor&& accessor)
{
return ReflectionData<Class, std::remove_cvref_t<Accessor>>{
std::forward<Accessor>(accessor)};
}

template <typename Class, typename Accessor>
auto makeReflectionData(std::string name, Accessor&& accessor)
{
return ReflectionData<Class, std::remove_cvref_t<Accessor>>{
std::move(name), std::forward<Accessor>(accessor)};
}

template <typename Class, typename Member>
auto makeReflectionData(Member Class::*member)
{
return makeReflectionData<Class>([member](auto& obj) -> auto&
{ return obj.*member; });
}

template <typename Class, typename Member>
auto makeReflectionData(std::string name, Member Class::*member)
{
return makeReflectionData<Class>(
std::move(name), [member](auto& obj) -> auto& { return obj.*member; });
}

template <typename Class, typename Member>
std::tuple<ReflectionData<Class, Member>> 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 <typename Class, typename... Accessors>
auto reflectWithoutName(Accessors&&... accessors)
{
return {{name, field}};
return std::tuple{
makeReflectionData<Class>(std::forward<Accessors>(accessors))...};
}

template <typename Class, typename... Members>
auto reflectWithoutName(Members Class::*... members)
{
return std::tuple{ReflectionData<Class, Members>{"", members}...};
return std::tuple{makeReflectionData<Class>(members)...};
}
} // namespace ProcessLib::Reflection
123 changes: 101 additions & 22 deletions ProcessLib/Reflection/ReflectionIPData.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,55 @@ namespace ProcessLib::Reflection
{
namespace detail
{
// Used in metaprogramming to check if the type T has a member "reflect".
template <typename T>
constexpr bool has_reflect = requires
concept has_reflect = requires { T::reflect(); };

template <typename... Ts>
auto reflect(std::type_identity<std::tuple<Ts...>>)
{
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<mp_list<Ts...>>::value);

return reflectWithoutName<std::tuple<Ts...>>(
[](auto& tuple_) -> auto& { return std::get<Ts>(tuple_); }...);
}

template <has_reflect T>
auto reflect(std::type_identity<T>)
{
return T::reflect();
}

template <typename T>
concept is_reflectable = requires {
ProcessLib::Reflection::detail::reflect(std::type_identity<T>{});
};

/**
* 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 <typename T>
struct is_raw_data : std::false_type
{
};

template <>
struct is_raw_data<double> : std::true_type
{
};

template <int N>
struct is_raw_data<Eigen::Matrix<double, N, 1, Eigen::ColMajor, N, 1>>
: std::true_type
{
T::reflect();
};

template <typename T>
Expand Down Expand Up @@ -118,6 +162,9 @@ struct GetFlattenedIPDataFromLocAsm
using ConcreteIPData = std::remove_cvref_t<
std::invoke_result_t<Accessor_CurrentLevelFromIPDataVecElement,
IPDataVectorElement const&>>;
static_assert(is_raw_data<ConcreteIPData>::value,
"This method only deals with raw data. The given "
"ConcreteIPData is not raw data.");

constexpr unsigned num_comp = NumberOfComponents<ConcreteIPData>::value;
auto const& ip_data_vector = accessor_ip_data_vec_in_loc_asm(loc_asm);
Expand All @@ -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 ==
Expand Down Expand Up @@ -216,28 +262,35 @@ void forEachReflectedFlattenedIPDataAccessor(
reflection_data,
[&accessor_ip_data_vec_in_loc_asm,
&accessor_current_level_from_ip_data_vec_element,
&callback]<typename Class, typename Member>(
ReflectionData<Class, Member> const& refl_data)
&callback]<typename Class, typename Accessor>(
ReflectionData<Class, Accessor> const& refl_data)
{
using MemberRef = std::invoke_result_t<Accessor, Class const&>;
using Member = std::remove_cvref_t<MemberRef>;

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<Member>)
if constexpr (is_reflectable<Member>)
{
forEachReflectedFlattenedIPDataAccessor<Dim>(
callback, Member::reflect(),
callback, detail::reflect(std::type_identity<Member>{}),
accessor_ip_data_vec_in_loc_asm,
accessor_member_from_ip_data_vec_element);
}
else
{
static_assert(is_raw_data<Member>::value,
"The current member is not reflectable, so we "
"expect it to be raw data.");

constexpr unsigned num_comp = NumberOfComponents<Member>::value;

callback(refl_data.name, num_comp,
Expand Down Expand Up @@ -280,24 +333,50 @@ void forEachReflectedFlattenedIPDataAccessor(ReflData const& reflection_data,
{
boost::mp11::tuple_for_each(
reflection_data,
[&callback]<typename Class, typename Member>(
ReflectionData<Class, std::vector<Member>> const& refl_data)
[&callback]<typename Class, typename Accessor>(
ReflectionData<Class, Accessor> 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<Class, LocAsmIF>,
"The currently processed reflection data is not for "
"the given LocAsmIF but for a different class.");

if constexpr (detail::has_reflect<Member>)
using AccessorResultRef =
std::invoke_result_t<Accessor, Class const&>;
using AccessorResult = std::remove_cvref_t<AccessorResultRef>;

// AccessorResult must be a std::vector<SomeType, SomeAllocator>. We
// check that, now.
static_assert(boost::mp11::mp_is_list<AccessorResult>::
value); // std::vector<SomeType, SomeAllocator>
// is a list in the Boost MP11 sense
static_assert(
std::is_same_v<
AccessorResult,
boost::mp11::mp_rename<AccessorResult, std::vector>>,
"We expect a std::vector, here.");
// Now, we know that AccessorResult is std::vector<Member>. To be
// more specific, AccessorResult is a std::vector<IPData> 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<Member>)
{
detail::forEachReflectedFlattenedIPDataAccessor<Dim>(
callback, Member::reflect(),
callback,
detail::reflect(std::type_identity<Member>{}),
accessor_ip_data_vec_in_loc_asm);
}
else
{
static_assert(detail::is_raw_data<Member>::value,
"The current member is not reflectable, so we "
"expect it to be raw data.");

constexpr unsigned num_comp =
detail::NumberOfComponents<Member>::value;

Expand Down
51 changes: 32 additions & 19 deletions ProcessLib/Reflection/ReflectionSetIPData.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ void setIPData(double const* values,
{
using AccessorResult = std::invoke_result_t<Accessor, IPData&>;
using AccessorResultStripped = std::remove_cvref_t<AccessorResult>;

static_assert(is_raw_data<AccessorResultStripped>::value,
"This method only deals with raw data. The given "
"AccessorResultStripped is not raw data.");

constexpr auto num_comp = NumberOfComponents<AccessorResultStripped>::value;

auto constexpr kv_size =
Expand Down Expand Up @@ -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 <int dim, typename IPData, typename Accessor_CurrentLevelFromIPData,
typename Class, typename Member>
typename Class, typename Accessor>
bool setIPDataIfNameMatches(std::string const& name, double const* values,
std::vector<IPData>& ip_data_vector,
Accessor_CurrentLevelFromIPData const& accessor,
ReflectionData<Class, Member> const& refl_data)
ReflectionData<Class, Accessor> 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<Accessor, Class&>;
using Member = std::remove_cvref_t<MemberRef>;

if constexpr (detail::has_reflect<Member>)
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<Member>)
{
return reflectSetIPData<dim>(name, values, ip_data_vector,
accessor_field_from_ip_data,
Member::reflect());
return reflectSetIPData<dim>(
name, values, ip_data_vector, accessor_field_from_ip_data,
detail::reflect(std::type_identity<Member>{}));
}
else
{
static_assert(detail::is_raw_data<Member>::value,
"The current member is not reflectable, so we "
"expect it to be raw data.");

if (refl_data.name != name)
{
return false;
Expand All @@ -98,12 +110,12 @@ bool setIPDataIfNameMatches(std::string const& name, double const* values,
}

template <int dim, typename IPData, typename Accessor_CurrentLevelFromIPData,
typename... Class, typename... Member, std::size_t... Idcs>
typename... Classes, typename... Accessors, std::size_t... Idcs>
bool reflectSetIPData(
std::string const& name, double const* values,
std::vector<IPData>& ip_data_vector,
Accessor_CurrentLevelFromIPData const& accessor,
std::tuple<ReflectionData<Class, Member>...> const& refl_data,
std::tuple<ReflectionData<Classes, Accessors>...> const& refl_data,
std::index_sequence<Idcs...>)
{
// uses short-circuit evaluation of the fold || ... to stop after the first
Expand All @@ -114,16 +126,16 @@ bool reflectSetIPData(
}

template <int dim, typename IPData, typename Accessor_CurrentLevelFromIPData,
typename... Class, typename... Member>
typename... Classes, typename... Accessors>
bool reflectSetIPData(
std::string const& name, double const* values,
std::vector<IPData>& ip_data_vector,
Accessor_CurrentLevelFromIPData const& accessor,
std::tuple<ReflectionData<Class, Member>...> const& refl_data)
std::tuple<ReflectionData<Classes, Accessors>...> const& refl_data)
{
return reflectSetIPData<dim>(name, values, ip_data_vector, accessor,
refl_data,
std::make_index_sequence<sizeof...(Class)>{});
return reflectSetIPData<dim>(
name, values, ip_data_vector, accessor, refl_data,
std::make_index_sequence<sizeof...(Classes)>{});
}
} // namespace detail

Expand All @@ -140,8 +152,9 @@ template <int dim, typename IPData>
std::size_t reflectSetIPData(std::string const& name, double const* values,
std::vector<IPData>& ip_data_vector)
{
detail::reflectSetIPData<dim>(name, values, ip_data_vector, std::identity{},
IPData::reflect());
detail::reflectSetIPData<dim>(
name, values, ip_data_vector, std::identity{},
detail::reflect(std::type_identity<IPData>{}));

return ip_data_vector.size();
}
Expand Down
Loading

0 comments on commit 980e84e

Please sign in to comment.