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