Warning: This file is not a C or C++ file. It does not have highlighting.
1 | //===-- include/flang/Common/template.h -------------------------*- C++ -*-===// |
---|---|
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #ifndef FORTRAN_COMMON_TEMPLATE_H_ |
10 | #define FORTRAN_COMMON_TEMPLATE_H_ |
11 | |
12 | #include "variant.h" |
13 | #include "flang/Common/idioms.h" |
14 | #include <functional> |
15 | #include <optional> |
16 | #include <tuple> |
17 | #include <type_traits> |
18 | #include <vector> |
19 | |
20 | // Utility templates for metaprogramming and for composing the |
21 | // std::optional<>, std::tuple<>, and std::variant<> containers. |
22 | |
23 | namespace Fortran::common { |
24 | |
25 | // SearchTypeList<PREDICATE, TYPES...> scans a list of types. The zero-based |
26 | // index of the first type T in the list for which PREDICATE<T>::value() is |
27 | // true is returned, or -1 if the predicate is false for every type in the list. |
28 | // This is a compile-time operation; see SearchTypes below for a run-time form. |
29 | template <int N, template <typename> class PREDICATE, typename TUPLE> |
30 | struct SearchTypeListHelper { |
31 | static constexpr int value() { |
32 | if constexpr (N >= std::tuple_size_v<TUPLE>) { |
33 | return -1; |
34 | } else if constexpr (PREDICATE<std::tuple_element_t<N, TUPLE>>::value()) { |
35 | return N; |
36 | } else { |
37 | return SearchTypeListHelper<N + 1, PREDICATE, TUPLE>::value(); |
38 | } |
39 | } |
40 | }; |
41 | |
42 | template <template <typename> class PREDICATE, typename... TYPES> |
43 | constexpr int SearchTypeList{ |
44 | SearchTypeListHelper<0, PREDICATE, std::tuple<TYPES...>>::value()}; |
45 | |
46 | // TypeIndex<A, TYPES...> scans a list of types for simple type equality. |
47 | // The zero-based index of A in the list is returned, or -1 if A is not present. |
48 | template <typename A> struct MatchType { |
49 | template <typename B> struct Match { |
50 | static constexpr bool value() { |
51 | return std::is_same_v<std::decay_t<A>, std::decay_t<B>>; |
52 | } |
53 | }; |
54 | }; |
55 | |
56 | template <typename A, typename... TYPES> |
57 | constexpr int TypeIndex{SearchTypeList<MatchType<A>::template Match, TYPES...>}; |
58 | |
59 | // IsTypeInList<A, TYPES...> is a simple presence predicate. |
60 | template <typename A, typename... TYPES> |
61 | constexpr bool IsTypeInList{TypeIndex<A, TYPES...> >= 0}; |
62 | |
63 | // OverMembers extracts the list of types that constitute the alternatives |
64 | // of a std::variant or elements of a std::tuple and passes that list as |
65 | // parameter types to a given variadic template. |
66 | template <template <typename...> class, typename> struct OverMembersHelper; |
67 | template <template <typename...> class T, typename... Ts> |
68 | struct OverMembersHelper<T, std::variant<Ts...>> { |
69 | using type = T<Ts...>; |
70 | }; |
71 | template <template <typename...> class T, typename... Ts> |
72 | struct OverMembersHelper<T, std::tuple<Ts...>> { |
73 | using type = T<Ts...>; |
74 | }; |
75 | |
76 | template <template <typename...> class T, typename TUPLEorVARIANT> |
77 | using OverMembers = |
78 | typename OverMembersHelper<T, std::decay_t<TUPLEorVARIANT>>::type; |
79 | |
80 | // SearchMembers<PREDICATE> scans the types that constitute the alternatives |
81 | // of a std::variant instantiation or elements of a std::tuple. |
82 | // The zero-based index of the first type T among the alternatives for which |
83 | // PREDICATE<T>::value() is true is returned, or -1 when the predicate is false |
84 | // for every type in the set. |
85 | template <template <typename> class PREDICATE> struct SearchMembersHelper { |
86 | template <typename... Ts> struct Scanner { |
87 | static constexpr int value() { return SearchTypeList<PREDICATE, Ts...>; } |
88 | }; |
89 | }; |
90 | |
91 | template <template <typename> class PREDICATE, typename TUPLEorVARIANT> |
92 | constexpr int SearchMembers{ |
93 | OverMembers<SearchMembersHelper<PREDICATE>::template Scanner, |
94 | TUPLEorVARIANT>::value()}; |
95 | |
96 | template <typename A, typename TUPLEorVARIANT> |
97 | constexpr int FindMember{ |
98 | SearchMembers<MatchType<A>::template Match, TUPLEorVARIANT>}; |
99 | template <typename A, typename TUPLEorVARIANT> |
100 | constexpr bool HasMember{FindMember<A, TUPLEorVARIANT> >= 0}; |
101 | |
102 | // std::optional<std::optional<A>> -> std::optional<A> |
103 | template <typename A> |
104 | std::optional<A> JoinOptional(std::optional<std::optional<A>> &&x) { |
105 | if (x) { |
106 | return std::move(*x); |
107 | } |
108 | return std::nullopt; |
109 | } |
110 | |
111 | // Convert an std::optional to an ordinary pointer |
112 | template <typename A> const A *GetPtrFromOptional(const std::optional<A> &x) { |
113 | if (x) { |
114 | return &*x; |
115 | } else { |
116 | return nullptr; |
117 | } |
118 | } |
119 | |
120 | // Copy a value from one variant type to another. The types allowed in the |
121 | // source variant must all be allowed in the destination variant type. |
122 | template <typename TOV, typename FROMV> TOV CopyVariant(const FROMV &u) { |
123 | return common::visit([](const auto &x) -> TOV { return {x}; }, u); |
124 | } |
125 | |
126 | // Move a value from one variant type to another. The types allowed in the |
127 | // source variant must all be allowed in the destination variant type. |
128 | template <typename TOV, typename FROMV> |
129 | common::IfNoLvalue<TOV, FROMV> MoveVariant(FROMV &&u) { |
130 | return common::visit( |
131 | [](auto &&x) -> TOV { return {std::move(x)}; }, std::move(u)); |
132 | } |
133 | |
134 | // CombineTuples takes a list of std::tuple<> template instantiation types |
135 | // and constructs a new std::tuple type that concatenates all of their member |
136 | // types. E.g., |
137 | // CombineTuples<std::tuple<char, int>, std::tuple<float, double>> |
138 | // is std::tuple<char, int, float, double>. |
139 | template <typename... TUPLES> struct CombineTuplesHelper { |
140 | static decltype(auto) f(TUPLES *...a) { |
141 | return std::tuple_cat(std::move(*a)...); |
142 | } |
143 | using type = decltype(f(static_cast<TUPLES *>(nullptr)...)); |
144 | }; |
145 | template <typename... TUPLES> |
146 | using CombineTuples = typename CombineTuplesHelper<TUPLES...>::type; |
147 | |
148 | // CombineVariants takes a list of std::variant<> instantiations and constructs |
149 | // a new instantiation that holds all of their alternatives, which must be |
150 | // pairwise distinct. |
151 | template <typename> struct VariantToTupleHelper; |
152 | template <typename... Ts> struct VariantToTupleHelper<std::variant<Ts...>> { |
153 | using type = std::tuple<Ts...>; |
154 | }; |
155 | template <typename VARIANT> |
156 | using VariantToTuple = typename VariantToTupleHelper<VARIANT>::type; |
157 | |
158 | template <typename A, typename... REST> struct AreTypesDistinctHelper { |
159 | static constexpr bool value() { |
160 | if constexpr (sizeof...(REST) > 0) { |
161 | // extra () for clang-format |
162 | return ((... && !std::is_same_v<A, REST>)) && |
163 | AreTypesDistinctHelper<REST...>::value(); |
164 | } |
165 | return true; |
166 | } |
167 | }; |
168 | template <typename... Ts> |
169 | constexpr bool AreTypesDistinct{AreTypesDistinctHelper<Ts...>::value()}; |
170 | |
171 | template <typename A, typename... Ts> struct AreSameTypeHelper { |
172 | using type = A; |
173 | static constexpr bool value() { |
174 | if constexpr (sizeof...(Ts) == 0) { |
175 | return true; |
176 | } else { |
177 | using Rest = AreSameTypeHelper<Ts...>; |
178 | return std::is_same_v<type, typename Rest::type> && Rest::value(); |
179 | } |
180 | } |
181 | }; |
182 | |
183 | template <typename... Ts> |
184 | constexpr bool AreSameType{AreSameTypeHelper<Ts...>::value()}; |
185 | |
186 | template <typename> struct TupleToVariantHelper; |
187 | template <typename... Ts> struct TupleToVariantHelper<std::tuple<Ts...>> { |
188 | static_assert(AreTypesDistinct<Ts...>, |
189 | "TupleToVariant: types are not pairwise distinct"); |
190 | using type = std::variant<Ts...>; |
191 | }; |
192 | template <typename TUPLE> |
193 | using TupleToVariant = typename TupleToVariantHelper<TUPLE>::type; |
194 | |
195 | template <typename... VARIANTS> struct CombineVariantsHelper { |
196 | using type = TupleToVariant<CombineTuples<VariantToTuple<VARIANTS>...>>; |
197 | }; |
198 | template <typename... VARIANTS> |
199 | using CombineVariants = typename CombineVariantsHelper<VARIANTS...>::type; |
200 | |
201 | // SquashVariantOfVariants: given a std::variant whose alternatives are |
202 | // all std::variant instantiations, form a new union over their alternatives. |
203 | template <typename VARIANT> |
204 | using SquashVariantOfVariants = OverMembers<CombineVariants, VARIANT>; |
205 | |
206 | // Given a type function, MapTemplate applies it to each of the types |
207 | // in a tuple or variant, and collect the results in a given variadic |
208 | // template (typically a std::variant). |
209 | template <template <typename> class, template <typename...> class, typename...> |
210 | struct MapTemplateHelper; |
211 | template <template <typename> class F, template <typename...> class PACKAGE, |
212 | typename... Ts> |
213 | struct MapTemplateHelper<F, PACKAGE, std::tuple<Ts...>> { |
214 | using type = PACKAGE<F<Ts>...>; |
215 | }; |
216 | template <template <typename> class F, template <typename...> class PACKAGE, |
217 | typename... Ts> |
218 | struct MapTemplateHelper<F, PACKAGE, std::variant<Ts...>> { |
219 | using type = PACKAGE<F<Ts>...>; |
220 | }; |
221 | template <template <typename> class F, typename TUPLEorVARIANT, |
222 | template <typename...> class PACKAGE = std::variant> |
223 | using MapTemplate = |
224 | typename MapTemplateHelper<F, PACKAGE, TUPLEorVARIANT>::type; |
225 | |
226 | // std::tuple<std::optional<>...> -> std::optional<std::tuple<...>> |
227 | // i.e., inverts a tuple of optional values into an optional tuple that has |
228 | // a value only if all of the original elements were present. |
229 | template <typename... A, std::size_t... J> |
230 | std::optional<std::tuple<A...>> AllElementsPresentHelper( |
231 | std::tuple<std::optional<A>...> &&t, std::index_sequence<J...>) { |
232 | bool present[]{std::get<J>(t).has_value()...}; |
233 | for (std::size_t j{0}; j < sizeof...(J); ++j) { |
234 | if (!present[j]) { |
235 | return std::nullopt; |
236 | } |
237 | } |
238 | return {std::make_tuple(*std::get<J>(t)...)}; |
239 | } |
240 | |
241 | template <typename... A> |
242 | std::optional<std::tuple<A...>> AllElementsPresent( |
243 | std::tuple<std::optional<A>...> &&t) { |
244 | return AllElementsPresentHelper( |
245 | std::move(t), std::index_sequence_for<A...>{}); |
246 | } |
247 | |
248 | // std::vector<std::optional<A>> -> std::optional<std::vector<A>> |
249 | // i.e., inverts a vector of optional values into an optional vector that |
250 | // will have a value only when all of the original elements are present. |
251 | template <typename A> |
252 | std::optional<std::vector<A>> AllElementsPresent( |
253 | std::vector<std::optional<A>> &&v) { |
254 | for (const auto &maybeA : v) { |
255 | if (!maybeA) { |
256 | return std::nullopt; |
257 | } |
258 | } |
259 | std::vector<A> result; |
260 | for (auto &&maybeA : std::move(v)) { |
261 | result.emplace_back(std::move(*maybeA)); |
262 | } |
263 | return result; |
264 | } |
265 | |
266 | // (std::optional<>...) -> std::optional<std::tuple<...>> |
267 | // i.e., given some number of optional values, return a optional tuple of |
268 | // those values that is present only of all of the values were so. |
269 | template <typename... A> |
270 | std::optional<std::tuple<A...>> AllPresent(std::optional<A> &&...x) { |
271 | return AllElementsPresent(std::make_tuple(std::move(x)...)); |
272 | } |
273 | |
274 | // (f(A...) -> R) -> std::optional<A>... -> std::optional<R> |
275 | // Apply a function to optional arguments if all are present. |
276 | // N.B. If the function returns std::optional, MapOptional will return |
277 | // std::optional<std::optional<...>> and you will probably want to |
278 | // run it through JoinOptional to "squash" it. |
279 | template <typename R, typename... A> |
280 | std::optional<R> MapOptional( |
281 | std::function<R(A &&...)> &&f, std::optional<A> &&...x) { |
282 | if (auto args{AllPresent(std::move(x)...)}) { |
283 | return std::make_optional(std::apply(std::move(f), std::move(*args))); |
284 | } |
285 | return std::nullopt; |
286 | } |
287 | template <typename R, typename... A> |
288 | std::optional<R> MapOptional(R (*f)(A &&...), std::optional<A> &&...x) { |
289 | return MapOptional(std::function<R(A && ...)>{f}, std::move(x)...); |
290 | } |
291 | |
292 | // Given a VISITOR class of the general form |
293 | // struct VISITOR { |
294 | // using Result = ...; |
295 | // using Types = std::tuple<...>; |
296 | // template<typename T> Result Test() { ... } |
297 | // }; |
298 | // SearchTypes will traverse the element types in the tuple in order |
299 | // and invoke VISITOR::Test<T>() on each until it returns a value that |
300 | // casts to true. If no invocation of Test succeeds, SearchTypes will |
301 | // return a default value. |
302 | template <std::size_t J, typename VISITOR> |
303 | common::IfNoLvalue<typename VISITOR::Result, VISITOR> SearchTypesHelper( |
304 | VISITOR &&visitor, typename VISITOR::Result &&defaultResult) { |
305 | using Tuple = typename VISITOR::Types; |
306 | if constexpr (J < std::tuple_size_v<Tuple>) { |
307 | if (auto result{visitor.template Test<std::tuple_element_t<J, Tuple>>()}) { |
308 | return result; |
309 | } |
310 | return SearchTypesHelper<J + 1, VISITOR>( |
311 | std::move(visitor), std::move(defaultResult)); |
312 | } else { |
313 | return std::move(defaultResult); |
314 | } |
315 | } |
316 | |
317 | template <typename VISITOR> |
318 | common::IfNoLvalue<typename VISITOR::Result, VISITOR> SearchTypes( |
319 | VISITOR &&visitor, |
320 | typename VISITOR::Result defaultResult = typename VISITOR::Result{}) { |
321 | return SearchTypesHelper<0, VISITOR>( |
322 | std::move(visitor), std::move(defaultResult)); |
323 | } |
324 | } // namespace Fortran::common |
325 | #endif // FORTRAN_COMMON_TEMPLATE_H_ |
326 |
Warning: This file is not a C or C++ file. It does not have highlighting.