xref: /llvm-project/flang/include/flang/Common/template.h (revision 7860f970666f46184ad740db48a69882d62e64fc)
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 {
valueSearchTypeListHelper31   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 {
valueMatchType::Match50     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