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
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 SearchTypes below for a run-time form.
29template <int N, template <typename> class PREDICATE, typename TUPLE>
30struct 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
42template <template <typename> class PREDICATE, typename... TYPES>
43constexpr 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.
48template <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
56template <typename A, typename... TYPES>
57constexpr int TypeIndex{SearchTypeList<MatchType<A>::template Match, TYPES...>};
58
59// IsTypeInList<A, TYPES...> is a simple presence predicate.
60template <typename A, typename... TYPES>
61constexpr 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.
66template <template <typename...> class, typename> struct OverMembersHelper;
67template <template <typename...> class T, typename... Ts>
68struct OverMembersHelper<T, std::variant<Ts...>> {
69 using type = T<Ts...>;
70};
71template <template <typename...> class T, typename... Ts>
72struct OverMembersHelper<T, std::tuple<Ts...>> {
73 using type = T<Ts...>;
74};
75
76template <template <typename...> class T, typename TUPLEorVARIANT>
77using 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.
85template <template <typename> class PREDICATE> struct SearchMembersHelper {
86 template <typename... Ts> struct Scanner {
87 static constexpr int value() { return SearchTypeList<PREDICATE, Ts...>; }
88 };
89};
90
91template <template <typename> class PREDICATE, typename TUPLEorVARIANT>
92constexpr int SearchMembers{
93 OverMembers<SearchMembersHelper<PREDICATE>::template Scanner,
94 TUPLEorVARIANT>::value()};
95
96template <typename A, typename TUPLEorVARIANT>
97constexpr int FindMember{
98 SearchMembers<MatchType<A>::template Match, TUPLEorVARIANT>};
99template <typename A, typename TUPLEorVARIANT>
100constexpr bool HasMember{FindMember<A, TUPLEorVARIANT> >= 0};
101
102// std::optional<std::optional<A>> -> std::optional<A>
103template <typename A>
104std::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
112template <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.
122template <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.
128template <typename TOV, typename FROMV>
129common::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>.
139template <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};
145template <typename... TUPLES>
146using 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.
151template <typename> struct VariantToTupleHelper;
152template <typename... Ts> struct VariantToTupleHelper<std::variant<Ts...>> {
153 using type = std::tuple<Ts...>;
154};
155template <typename VARIANT>
156using VariantToTuple = typename VariantToTupleHelper<VARIANT>::type;
157
158template <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};
168template <typename... Ts>
169constexpr bool AreTypesDistinct{AreTypesDistinctHelper<Ts...>::value()};
170
171template <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
183template <typename... Ts>
184constexpr bool AreSameType{AreSameTypeHelper<Ts...>::value()};
185
186template <typename> struct TupleToVariantHelper;
187template <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};
192template <typename TUPLE>
193using TupleToVariant = typename TupleToVariantHelper<TUPLE>::type;
194
195template <typename... VARIANTS> struct CombineVariantsHelper {
196 using type = TupleToVariant<CombineTuples<VariantToTuple<VARIANTS>...>>;
197};
198template <typename... VARIANTS>
199using 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.
203template <typename VARIANT>
204using 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).
209template <template <typename> class, template <typename...> class, typename...>
210struct MapTemplateHelper;
211template <template <typename> class F, template <typename...> class PACKAGE,
212 typename... Ts>
213struct MapTemplateHelper<F, PACKAGE, std::tuple<Ts...>> {
214 using type = PACKAGE<F<Ts>...>;
215};
216template <template <typename> class F, template <typename...> class PACKAGE,
217 typename... Ts>
218struct MapTemplateHelper<F, PACKAGE, std::variant<Ts...>> {
219 using type = PACKAGE<F<Ts>...>;
220};
221template <template <typename> class F, typename TUPLEorVARIANT,
222 template <typename...> class PACKAGE = std::variant>
223using 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.
229template <typename... A, std::size_t... J>
230std::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
241template <typename... A>
242std::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.
251template <typename A>
252std::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.
269template <typename... A>
270std::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.
279template <typename R, typename... A>
280std::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}
287template <typename R, typename... A>
288std::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.
302template <std::size_t J, typename VISITOR>
303common::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
317template <typename VISITOR>
318common::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.

source code of flang/include/flang/Common/template.h