OGS
CheckEvalOrderRT.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
4#pragma once
5
6#include <boost/mp11.hpp>
7#include <typeindex>
8#include <unordered_set>
9
10#include "Apply.h"
12#include "BaseLib/Logging.h"
13
14namespace ProcessLib::Graph
15{
16namespace detail
17{
18template <typename T>
20 : boost::mp11::mp_bool<std::is_lvalue_reference_v<T> &&
21 std::is_const_v<std::remove_reference_t<T>>>
22{
23 static_assert(std::is_lvalue_reference_v<T>,
24 "The current implementation only deals with l-value "
25 "references as function arguments. If you want to extend it, "
26 "test thoroughly in order to not introduce bugs.");
27};
28
29template <typename T>
30struct IsOutputArgument : boost::mp11::mp_bool<!IsInputArgument<T>::value>
31{
32 static_assert(std::is_lvalue_reference_v<T>,
33 "The current implementation only deals with l-value "
34 "references as function arguments. If you want to extend it, "
35 "test thoroughly in order to not introduce bugs.");
36};
37
38template <typename Model>
39bool isEvalOrderCorrectRT(std::unordered_set<std::type_index>& computed_data)
40{
41 using namespace boost::mp11;
42
43 using ModelArgs =
44 typename GetFunctionArgumentTypes<decltype(&Model::eval)>::type;
45 using ModelInputs = mp_filter<IsInputArgument, ModelArgs>;
46 using ModelOutputs = mp_filter<IsOutputArgument, ModelArgs>;
47
48 using ModelInputsWrapped = mp_transform<mp_identity, ModelInputs>;
49
50 // Check that all inputs have already been computed before.
51 bool all_inputs_computed = true;
52 mp_for_each<ModelInputsWrapped>(
53 [&computed_data,
54 &all_inputs_computed]<typename Input>(mp_identity<Input>)
55 {
56 if (!computed_data.contains(std::type_index{typeid(Input)}))
57 {
58 ERR("Input {} of model {} has not been computed/set before the "
59 "model evaluation.",
62 all_inputs_computed = false;
63 }
64 });
65 if (!all_inputs_computed)
66 {
67 return false;
68 }
69
70 using ModelOutputsWrapped = mp_transform<mp_identity, ModelOutputs>;
71
72 // All outputs are "computed data", now.
73 bool no_output_precomputed = true;
74 mp_for_each<ModelOutputsWrapped>(
75 [&computed_data,
76 &no_output_precomputed]<typename Output>(mp_identity<Output>)
77 {
78 auto const [it, emplaced] = computed_data.emplace(typeid(Output));
79
80 if (!emplaced)
81 {
82 ERR("Output {} of model {} is computed more than once.",
85 no_output_precomputed = false;
86 }
87 });
88
89 return no_output_precomputed;
90}
91
92template <typename... Models>
93bool isEvalOrderCorrectRT(boost::mp11::mp_list<Models...>,
94 std::unordered_set<std::type_index>&& computed_data)
95{
96 return (isEvalOrderCorrectRT<Models>(computed_data) && ...);
97}
98} // namespace detail
99
108template <typename Models, typename Inputs>
109bool isEvalOrderCorrectRT() // RT for runtime
110{
111 using namespace boost::mp11;
112
113 static_assert(mp_is_list<Models>::value);
114 static_assert(mp_is_list<Inputs>::value);
115
116 // Wrap inputs. The elements of InputsWrapped are default constructible.
117 using InputsWrapped = mp_transform<mp_identity, Inputs>;
118
119 // "Holds" all data that has been computed successively by the invoked
120 // models.
121 std::unordered_set<std::type_index> computed_data;
122
123 // All inputs are considered "computed data".
124 mp_for_each<InputsWrapped>(
125 [&computed_data]<typename Input>(mp_identity<Input>)
126 { computed_data.emplace(typeid(Input)); });
127
128 return detail::isEvalOrderCorrectRT(mp_rename<Models, mp_list>{},
129 std::move(computed_data));
130}
131} // namespace ProcessLib::Graph
void ERR(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:40
std::string typeToString()
bool isEvalOrderCorrectRT(std::unordered_set< std::type_index > &computed_data)