Line data Source code
1 1 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : /// \file
5 : /// Utilities to retrieve values from maps in tagged containers
6 :
7 : #pragma once
8 :
9 : #include <tuple>
10 : #include <utility>
11 :
12 : #include "DataStructures/DataBox/DataBox.hpp"
13 : #include "Utilities/ForceInline.hpp"
14 : #include "Utilities/Gsl.hpp"
15 : #include "Utilities/TMPL.hpp"
16 : #include "Utilities/TaggedTuple.hpp"
17 : #include "Utilities/TupleSlice.hpp"
18 : #include "Utilities/TypeTraits/CreateIsCallable.hpp"
19 : #include "Utilities/TypeTraits/IsMaplike.hpp"
20 :
21 0 : namespace elliptic::util {
22 : namespace detail {
23 : CREATE_IS_CALLABLE(apply)
24 : CREATE_IS_CALLABLE_V(apply)
25 :
26 : template <typename F, typename Args>
27 : struct ApplyImpl;
28 :
29 : template <typename F, typename... Args>
30 : struct ApplyImpl<F, std::tuple<Args...>> {
31 : // Dispatch to static apply function or call operator, whichever is available
32 : static constexpr decltype(auto) apply(F&& f, Args&&... args) {
33 : if constexpr (is_apply_callable_v<F, Args...>) {
34 : return F::apply(std::forward<Args>(args)...);
35 : } else {
36 : return std::forward<F>(f)(std::forward<Args>(args)...);
37 : }
38 : }
39 : };
40 :
41 : template <bool PassThrough, typename... MapKeys>
42 : struct unmap_arg;
43 :
44 : template <typename... MapKeys>
45 : struct unmap_arg<true, MapKeys...> {
46 : template <typename T>
47 : static constexpr const T& apply(const T& arg,
48 : const std::tuple<MapKeys...>& /*map_keys*/) {
49 : return arg;
50 : }
51 :
52 : template <typename T>
53 : static constexpr gsl::not_null<T*> apply(
54 : const gsl::not_null<T*> arg, const std::tuple<MapKeys...>& /*map_keys*/) {
55 : return arg;
56 : }
57 : };
58 :
59 : template <typename FirstMapKey, typename... MapKeys>
60 : struct unmap_arg<false, FirstMapKey, MapKeys...> {
61 : template <typename T>
62 : static constexpr decltype(auto) apply(
63 : const T& arg, const std::tuple<FirstMapKey, MapKeys...>& map_keys) {
64 : return unmap_arg<((sizeof...(MapKeys) == 0) or
65 : (not tt::is_maplike_v<typename T::mapped_type>)),
66 : MapKeys...>::apply(arg.at(std::get<0>(map_keys)),
67 : tuple_tail<sizeof...(MapKeys)>(
68 : map_keys));
69 : }
70 :
71 : template <typename T>
72 : static constexpr decltype(auto) apply(
73 : const gsl::not_null<T*> arg,
74 : const std::tuple<FirstMapKey, MapKeys...>& map_keys) {
75 : return unmap_arg<((sizeof...(MapKeys) == 0) or
76 : (not tt::is_maplike_v<typename T::mapped_type>)),
77 : MapKeys...>::apply(make_not_null(&arg->
78 : operator[](std::get<0>(
79 : map_keys))),
80 : tuple_tail<sizeof...(MapKeys)>(
81 : map_keys));
82 : }
83 : };
84 :
85 : template <typename... ArgumentTags, typename PassthroughArgumentTags,
86 : typename F, typename TaggedContainer, typename... MapKeys,
87 : typename... Args>
88 : SPECTRE_ALWAYS_INLINE decltype(auto) apply_at(
89 : F&& f, const TaggedContainer& box, const std::tuple<MapKeys...>& map_keys,
90 : tmpl::list<ArgumentTags...> /*meta*/, PassthroughArgumentTags /*meta*/,
91 : Args&&... args) {
92 : using ::db::apply;
93 : using ::tuples::apply;
94 : return apply<tmpl::list<ArgumentTags...>>(
95 : [&f, &map_keys, &args...](const auto&... args_items) {
96 : (void)map_keys;
97 : return std::forward<F>(f)(
98 : unmap_arg<
99 : tmpl::list_contains_v<PassthroughArgumentTags, ArgumentTags>,
100 : MapKeys...>::apply(args_items, map_keys)...,
101 : std::forward<Args>(args)...);
102 : },
103 : box);
104 : }
105 :
106 : } // namespace detail
107 :
108 : /// @{
109 : /*!
110 : * \brief Apply the invokable `f` with arguments from maps in the DataBox
111 : *
112 : * Retrieves the `ArgumentTags` from the DataBox, evaluates them at the
113 : * `map_key(s)` (by calling their `at` member function for every map key in
114 : * turn) and calls the invokable `f` with the unmapped arguments. The tags in
115 : * `PassthroughArgumentTags` are passed directly to `f` without unmapping them.
116 : *
117 : * For example, a DataBox may have these tags of which two are maps:
118 : *
119 : * \snippet Test_ApplyAt.cpp apply_at_tags
120 : *
121 : * You can use `apply_at` to evaluate a function at a particular key for these
122 : * maps:
123 : *
124 : * \snippet Test_ApplyAt.cpp apply_at_example
125 : *
126 : * \see db::apply
127 : */
128 : template <typename ArgumentTags, typename PassthroughArgumentTags,
129 : typename... MapKeys, typename F, typename TaggedContainer,
130 : typename... Args>
131 1 : SPECTRE_ALWAYS_INLINE decltype(auto) apply_at(
132 : F&& f, const TaggedContainer& box, const std::tuple<MapKeys...>& map_keys,
133 : Args&&... args) {
134 : return detail::apply_at(std::forward<F>(f), box, map_keys, ArgumentTags{},
135 : PassthroughArgumentTags{},
136 : std::forward<Args>(args)...);
137 : }
138 :
139 : template <typename ArgumentTags, typename PassthroughArgumentTags,
140 : typename MapKey, typename F, typename TaggedContainer,
141 : typename... Args>
142 1 : SPECTRE_ALWAYS_INLINE decltype(auto) apply_at(F&& f, const TaggedContainer& box,
143 : const MapKey& map_key,
144 : Args&&... args) {
145 : return detail::apply_at(
146 : std::forward<F>(f), box, std::forward_as_tuple(map_key), ArgumentTags{},
147 : PassthroughArgumentTags{}, std::forward<Args>(args)...);
148 : }
149 : /// @}
150 :
151 : namespace detail {
152 :
153 : template <typename... ReturnTags, typename... ArgumentTags,
154 : typename PassthroughTags, typename F, typename TaggedContainer,
155 : typename... MapKeys, typename... Args>
156 : SPECTRE_ALWAYS_INLINE void mutate_apply_at(
157 : F&& f, const gsl::not_null<TaggedContainer*> box,
158 : const std::tuple<MapKeys...>& map_keys, tmpl::list<ReturnTags...> /*meta*/,
159 : tmpl::list<ArgumentTags...> /*meta*/, PassthroughTags /*meta*/,
160 : Args&&... args) {
161 : using ::db::apply;
162 : using ::db::mutate_apply;
163 : using ::tuples::apply;
164 : const auto args_items = apply<tmpl::list<ArgumentTags...>>(
165 : [](const auto&... expanded_args_items) {
166 : return std::forward_as_tuple(expanded_args_items...);
167 : },
168 : *box);
169 : mutate_apply<tmpl::list<ReturnTags...>, tmpl::list<>>(
170 : [&f, &map_keys, &args_items, &args...](const auto... mutated_items) {
171 : (void)map_keys;
172 : (void)args_items;
173 : auto all_args = std::tuple_cat(
174 : std::make_tuple(
175 : unmap_arg<tmpl::list_contains_v<PassthroughTags, ReturnTags>,
176 : MapKeys...>::apply(mutated_items, map_keys)...),
177 : std::forward_as_tuple(
178 : unmap_arg<tmpl::list_contains_v<PassthroughTags, ArgumentTags>,
179 : MapKeys...>::
180 : apply(std::get<tmpl::index_of<tmpl::list<ArgumentTags...>,
181 : ArgumentTags>::value>(
182 : args_items),
183 : map_keys)...,
184 : args...));
185 : std::apply(
186 : [&f](auto&&... expanded_args) {
187 : ApplyImpl<F, std::decay_t<decltype(all_args)>>::apply(
188 : std::forward<F>(f), std::move(expanded_args)...);
189 : },
190 : std::move(all_args));
191 : },
192 : box);
193 : }
194 :
195 : } // namespace detail
196 :
197 : /// @{
198 : /*!
199 : * \brief Apply the invokable `f` to mutate items in maps in the DataBox
200 : *
201 : * Retrieves the `MutateTags` and `ArgumentTags` from the DataBox, evaluates
202 : * them at the `map_key(s)` (by calling their `at` member function for every map
203 : * key in turn) and calls the invokable `f` with the unmapped arguments. The
204 : * tags in `PassthroughTags` are passed directly to `f` without unmapping them.
205 : *
206 : * For example, a DataBox may have these tags of which two are maps:
207 : *
208 : * \snippet Test_ApplyAt.cpp apply_at_tags
209 : *
210 : * You can use `mutate_apply_at` to mutate items at a particular key for these
211 : * maps:
212 : *
213 : * \snippet Test_ApplyAt.cpp mutate_apply_at_example
214 : *
215 : * \see db::mutate_apply
216 : */
217 : template <typename MutateTags, typename ArgumentTags, typename PassthroughTags,
218 : typename F, typename TaggedContainer, typename... MapKeys,
219 : typename... Args>
220 1 : SPECTRE_ALWAYS_INLINE void mutate_apply_at(
221 : F&& f, const gsl::not_null<TaggedContainer*> box,
222 : const std::tuple<MapKeys...>& map_keys, Args&&... args) {
223 : detail::mutate_apply_at(std::forward<F>(f), box, map_keys, MutateTags{},
224 : ArgumentTags{}, PassthroughTags{},
225 : std::forward<Args>(args)...);
226 : }
227 :
228 : template <typename MutateTags, typename ArgumentTags, typename PassthroughTags,
229 : typename F, typename TaggedContainer, typename MapKey,
230 : typename... Args>
231 1 : SPECTRE_ALWAYS_INLINE void mutate_apply_at(
232 : F&& f, const gsl::not_null<TaggedContainer*> box, const MapKey& map_key,
233 : Args&&... args) {
234 : detail::mutate_apply_at(
235 : std::forward<F>(f), box, std::forward_as_tuple(map_key), MutateTags{},
236 : ArgumentTags{}, PassthroughTags{}, std::forward<Args>(args)...);
237 : }
238 : /// @}
239 :
240 : } // namespace elliptic::util
|