Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <string>
7 : #include <tuple>
8 :
9 : #include "DataStructures/DataBox/DataBoxTag.hpp"
10 : #include "DataStructures/DataBox/IsApplyCallable.hpp"
11 : #include "DataStructures/DataBox/TagName.hpp"
12 : #include "Utilities/CleanupRoutine.hpp"
13 : #include "Utilities/ErrorHandling/Error.hpp"
14 : #include "Utilities/ForceInline.hpp"
15 : #include "Utilities/Gsl.hpp"
16 : #include "Utilities/PrettyType.hpp"
17 : #include "Utilities/TMPL.hpp"
18 : #include "Utilities/TypeTraits/IsCallable.hpp"
19 :
20 1 : namespace db {
21 : /*!
22 : * \brief A class for retrieving items from a DataBox without needing to know
23 : * all the tags in the box.
24 : *
25 : * Retrieval is handled using a virtual function call but still uses Tags
26 : * rather than strings to ensure automatic handling of casting to the expected
27 : * type.
28 : */
29 1 : class Access {
30 : public:
31 0 : virtual ~Access() = default;
32 :
33 : /// Print the expanded type aliases of the derived `db::DataBox`
34 1 : virtual std::string print_tags() const = 0;
35 :
36 : /// Print the expanded type aliases of the derived `db::DataBox`
37 1 : virtual std::string print_types() const = 0;
38 :
39 : private:
40 : template <typename Tag>
41 1 : friend const auto& get(const Access& box);
42 : template <typename... MutateTags, typename Invokable, typename... Args>
43 0 : friend decltype(auto) mutate(Invokable&& invokable,
44 : gsl::not_null<Access*> box, Args&&... args);
45 0 : virtual void* mutate_item_by_name(const std::string& tag_name) = 0;
46 0 : virtual const void* get_item_by_name(const std::string& tag_name) const = 0;
47 0 : virtual bool lock_box_for_mutate() = 0;
48 0 : virtual void unlock_box_after_mutate() = 0;
49 0 : virtual void mutate_mutable_subitems(const std::string& tag_name) = 0;
50 0 : virtual void reset_compute_items_after_mutate(
51 : const std::string& tag_name) = 0;
52 :
53 : template <typename Tag>
54 0 : auto mutate() -> gsl::not_null<typename Tag::type*> {
55 : static const std::string tag_name = pretty_type::get_name<Tag>();
56 : return {
57 : // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
58 : reinterpret_cast<typename Tag::type*>(mutate_item_by_name(tag_name))};
59 : }
60 : };
61 :
62 : /// \brief Retrieve a tag from a `db::Access`
63 : template <typename Tag>
64 1 : SPECTRE_ALWAYS_INLINE const auto& get(const Access& box) {
65 : static const std::string tag_name = pretty_type::get_name<Tag>();
66 : if constexpr (tt::is_a_v<std::unique_ptr, typename Tag::type>) {
67 : // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
68 : return *reinterpret_cast<const typename Tag::type::element_type*>(
69 : box.get_item_by_name(tag_name));
70 : } else {
71 : // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
72 : return *reinterpret_cast<const typename Tag::type*>(
73 : box.get_item_by_name(tag_name));
74 : }
75 : }
76 :
77 : /// \brief Mutate a tag in a `db::Access`.
78 : template <typename... MutateTags, typename Invokable, typename... Args>
79 1 : decltype(auto) mutate(Invokable&& invokable, const gsl::not_null<Access*> box,
80 : Args&&... args) {
81 : if (UNLIKELY(box->lock_box_for_mutate())) {
82 : ERROR(
83 : "Unable to mutate a DataBox that is already being mutated. This "
84 : "error occurs when mutating a DataBox from inside the invokable "
85 : "passed to the mutate function.");
86 : }
87 :
88 : const CleanupRoutine unlock_box = [&box]() {
89 : box->unlock_box_after_mutate();
90 : };
91 :
92 : // Check that the arguments are fetchable before setting up the
93 : // cleanup so we don't trigger an error during stack unwinding.
94 : const std::tuple mutate_pointers{box->template mutate<MutateTags>()...};
95 :
96 : const CleanupRoutine reset_post_mutate = [&box]() {
97 : (void)box;
98 : EXPAND_PACK_LEFT_TO_RIGHT([&box]() {
99 : static const std::string tag_name = pretty_type::get_name<MutateTags>();
100 : box->mutate_mutable_subitems(tag_name);
101 : box->reset_compute_items_after_mutate(tag_name);
102 : }());
103 : };
104 : return std::apply(
105 : invokable,
106 : std::tuple_cat(mutate_pointers,
107 : std::forward_as_tuple(std::forward<Args>(args)...)));
108 : }
109 :
110 : namespace detail {
111 : template <typename... ReturnTags, typename... ArgumentTags, typename F,
112 : typename... Args>
113 : SPECTRE_ALWAYS_INLINE constexpr decltype(auto) mutate_apply(
114 : F&& f, const gsl::not_null<Access*> box, tmpl::list<ReturnTags...> /*meta*/,
115 : tmpl::list<ArgumentTags...> /*meta*/, Args&&... args) {
116 : static_assert(not(... or std::is_same_v<ArgumentTags, Tags::DataBox>),
117 : "Cannot pass Tags::DataBox to mutate_apply when mutating "
118 : "since the db::get won't work inside mutate_apply.");
119 : if constexpr (detail::is_apply_callable_v<
120 : F, const gsl::not_null<typename ReturnTags::type*>...,
121 : const_item_type<ArgumentTags, tmpl::list<>>..., Args...>) {
122 : return ::db::mutate<ReturnTags...>(
123 : [](const gsl::not_null<typename ReturnTags::type*>... mutated_items,
124 : const_item_type<ArgumentTags, tmpl::list<>>... args_items,
125 : decltype(std::forward<Args>(args))... l_args) {
126 : return std::decay_t<F>::apply(mutated_items..., args_items...,
127 : std::forward<Args>(l_args)...);
128 : },
129 : box, db::get<ArgumentTags>(*box)..., std::forward<Args>(args)...);
130 : } else if constexpr (::tt::is_callable_v<
131 : F,
132 : const gsl::not_null<typename ReturnTags::type*>...,
133 : const_item_type<ArgumentTags, tmpl::list<>>...,
134 : Args...>) {
135 : return ::db::mutate<ReturnTags...>(f, box, db::get<ArgumentTags>(*box)...,
136 : std::forward<Args>(args)...);
137 : } else {
138 : error_function_not_callable<F, gsl::not_null<typename ReturnTags::type*>...,
139 : const_item_type<ArgumentTags, tmpl::list<>>...,
140 : Args...>();
141 : }
142 : }
143 : } // namespace detail
144 :
145 : /// @{
146 : /*!
147 : * \ingroup DataBoxGroup
148 : * \brief Apply the invokable `f` mutating items `MutateTags` and taking as
149 : * additional arguments `ArgumentTags` and `args`.
150 : *
151 : * \details
152 : * `f` must either be invokable with the arguments of type
153 : * `gsl::not_null<db::item_type<MutateTags>*>...,
154 : * db::const_item_type<ArgumentTags>..., Args...`
155 : * where the first two pack expansions are over the elements in the typelists
156 : * `MutateTags` and `ArgumentTags`, or have a static `apply` function that is
157 : * callable with the same types. If the type of `f` specifies `return_tags` and
158 : * `argument_tags` typelists, these are used for the `MutateTags` and
159 : * `ArgumentTags`, respectively.
160 : *
161 : * Any return values of the invokable `f` are forwarded as returns to the
162 : * `mutate_apply` call.
163 : *
164 : * \example
165 : * An example of using `mutate_apply` with a lambda:
166 : * \snippet Test_DataBox.cpp mutate_apply_lambda_example
167 : *
168 : * An example of a class with a static `apply` function
169 : * \snippet Test_DataBox.cpp mutate_apply_struct_definition_example
170 : * and how to use `mutate_apply` with the above class
171 : * \snippet Test_DataBox.cpp mutate_apply_struct_example_stateful
172 : * Note that the class exposes `return_tags` and `argument_tags` typelists, so
173 : * we don't specify the template parameters explicitly.
174 : * If the class `F` has no state, like in this example,
175 : * \snippet Test_DataBox.cpp mutate_apply_struct_definition_example
176 : * you can also use the stateless overload of `mutate_apply`:
177 : * \snippet Test_DataBox.cpp mutate_apply_struct_example_stateless
178 : *
179 : * \tparam MutateTags typelist of Tags to mutate
180 : * \tparam ArgumentTags typelist of additional items to retrieve from the
181 : * `Access`
182 : * \tparam F The invokable to apply
183 : */
184 : template <typename MutateTags, typename ArgumentTags, typename F,
185 : typename... Args>
186 1 : SPECTRE_ALWAYS_INLINE constexpr decltype(auto) mutate_apply(
187 : F&& f, const gsl::not_null<Access*> box, Args&&... args) {
188 : return detail::mutate_apply(std::forward<F>(f), box, MutateTags{},
189 : ArgumentTags{}, std::forward<Args>(args)...);
190 : }
191 :
192 : template <typename F, typename... Args>
193 1 : SPECTRE_ALWAYS_INLINE constexpr decltype(auto) mutate_apply(
194 : F&& f, const gsl::not_null<Access*> box, Args&&... args) {
195 : return mutate_apply<typename std::decay_t<F>::return_tags,
196 : typename std::decay_t<F>::argument_tags>(
197 : std::forward<F>(f), box, std::forward<Args>(args)...);
198 : }
199 :
200 : template <typename F, typename... Args>
201 1 : SPECTRE_ALWAYS_INLINE constexpr decltype(auto) mutate_apply(
202 : const gsl::not_null<Access*> box, Args&&... args) {
203 : return mutate_apply(F{}, box, std::forward<Args>(args)...);
204 : }
205 : /// @}
206 : } // namespace db
|