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