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