// Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef FORTRAN_COMMON_TEMPLATE_H_ #define FORTRAN_COMMON_TEMPLATE_H_ #include #include #include #include #include #include // Utility templates for metaprogramming and for composing the // std::optional<>, std::tuple<>, and std::variant<> containers. namespace Fortran::common { // SearchTypeList scans a list of types. The zero-based // index of the first type T in the list for which PREDICATE::value() is // true is returned, or -1 if the predicate is false for every type in the list. // This is a compile-time operation; see SearchTypes below for a run-time form. template class PREDICATE, typename TUPLE> struct SearchTypeListHelper { static constexpr int value() { if constexpr (N >= std::tuple_size_v) { return -1; } else if constexpr (PREDICATE>::value()) { return N; } else { return SearchTypeListHelper::value(); } } }; template class PREDICATE, typename... TYPES> constexpr int SearchTypeList{ SearchTypeListHelper<0, PREDICATE, std::tuple>::value()}; // TypeIndex scans a list of types for simple type equality. // The zero-based index of A in the list is returned, or -1 if A is not present. template struct MatchType { template struct Match { static constexpr bool value() { return std::is_same_v, std::decay_t>; } }; }; template constexpr int TypeIndex{SearchTypeList::template Match, TYPES...>}; // IsTypeInList is a simple presence predicate. template constexpr bool IsTypeInList{TypeIndex >= 0}; // OverMembers extracts the list of types that constitute the alternatives // of a std::variant or elements of a std::tuple and passes that list as // parameter types to a given variadic template. template class, typename> struct OverMembersHelper; template class T, typename... Ts> struct OverMembersHelper> { using type = T; }; template class T, typename... Ts> struct OverMembersHelper> { using type = T; }; template class T, typename TUPLEorVARIANT> using OverMembers = typename OverMembersHelper>::type; // SearchMembers scans the types that constitute the alternatives // of a std::variant instantiation or elements of a std::tuple. // The zero-based index of the first type T among the alternatives for which // PREDICATE::value() is true is returned, or -1 when the predicate is false // for every type in the set. template class PREDICATE> struct SearchMembersHelper { template struct Scanner { static constexpr int value() { return SearchTypeList; } }; }; template class PREDICATE, typename TUPLEorVARIANT> constexpr int SearchMembers{ OverMembers::template Scanner, TUPLEorVARIANT>::value()}; template constexpr bool HasMember{ SearchMembers::template Match, TUPLEorVARIANT> >= 0}; // std::optional> -> std::optional template std::optional JoinOptional(std::optional> &&x) { if (x.has_value()) { return std::move(*x); } return std::nullopt; } // Copy a value from one variant type to another. The types allowed in the // source variant must all be allowed in the destination variant type. template TOV CopyVariant(const FROMV &u) { return std::visit([](const auto &x) -> TOV { return {x}; }, std::move(u)); } // Move a value from one variant type to another. The types allowed in the // source variant must all be allowed in the destination variant type. template common::IfNoLvalue MoveVariant(FROMV &&u) { return std::visit( [](auto &&x) -> TOV { return {std::move(x)}; }, std::move(u)); } // CombineTuples takes a list of std::tuple<> template instantiation types // and constructs a new std::tuple type that concatenates all of their member // types. E.g., // CombineTuples, std::tuple> // is std::tuple. template struct CombineTuplesHelper { static decltype(auto) f(TUPLES *... a) { return std::tuple_cat(std::move(*a)...); } using type = decltype(f(static_cast(nullptr)...)); }; template using CombineTuples = typename CombineTuplesHelper::type; // CombineVariants takes a list of std::variant<> instantiations and constructs // a new instantiation that holds all of their alternatives, which must be // pairwise distinct. template struct VariantToTupleHelper; template struct VariantToTupleHelper> { using type = std::tuple; }; template using VariantToTuple = typename VariantToTupleHelper::type; template struct AreTypesDistinctHelper { static constexpr bool value() { if constexpr (std::is_same_v) { return false; } if constexpr (sizeof...(REST) > 0) { return AreTypesDistinctHelper::value() && AreTypesDistinctHelper::value(); } return true; } }; template constexpr bool AreTypesDistinct{AreTypesDistinctHelper::value()}; template struct AreSameTypeHelper { using type = A; static constexpr bool value() { if constexpr (sizeof...(Ts) == 0) { return true; } else { using Rest = AreSameTypeHelper; return std::is_same_v && Rest::value(); } } }; template constexpr bool AreSameType{AreSameTypeHelper::value()}; template struct TupleToVariantHelper; template struct TupleToVariantHelper> { static_assert(AreTypesDistinct || !"TupleToVariant: types are not pairwise distinct"); using type = std::variant; }; template using TupleToVariant = typename TupleToVariantHelper::type; template struct CombineVariantsHelper { using type = TupleToVariant...>>; }; template using CombineVariants = typename CombineVariantsHelper::type; // SquashVariantOfVariants: given a std::variant whose alternatives are // all std::variant instantiations, form a new union over their alternatives. template using SquashVariantOfVariants = OverMembers; // Given a type function, MapTemplate applies it to each of the types // in a tuple or variant, and collect the results in a given variadic // template (typically a std::variant). template class, template class, typename...> struct MapTemplateHelper; template class F, template class PACKAGE, typename... Ts> struct MapTemplateHelper> { using type = PACKAGE...>; }; template class F, template class PACKAGE, typename... Ts> struct MapTemplateHelper> { using type = PACKAGE...>; }; template class F, typename TUPLEorVARIANT, template class PACKAGE = std::variant> using MapTemplate = typename MapTemplateHelper::type; // std::tuple...> -> std::optional> // i.e., inverts a tuple of optional values into an optional tuple that has // a value only if all of the original elements were present. template std::optional> AllElementsPresentHelper( std::tuple...> &&t, std::index_sequence) { bool present[]{std::get(t).has_value()...}; for (std::size_t j{0}; j < sizeof...(J); ++j) { if (!present[j]) { return std::nullopt; } } return {std::make_tuple(*std::get(t)...)}; } template std::optional> AllElementsPresent( std::tuple...> &&t) { return AllElementsPresentHelper( std::move(t), std::index_sequence_for{}); } // std::vector> -> std::optional> // i.e., inverts a vector of optional values into an optional vector that // will have a value only when all of the original elements are present. template std::optional> AllElementsPresent( std::vector> &&v) { for (const auto &maybeA : v) { if (!maybeA.has_value()) { return std::nullopt; } } std::vector result; for (auto &&maybeA : std::move(v)) { result.emplace_back(std::move(*maybeA)); } return result; } // (std::optional<>...) -> std::optional> // i.e., given some number of optional values, return a optional tuple of // those values that is present only of all of the values were so. template std::optional> AllPresent(std::optional &&... x) { return AllElementsPresent(std::make_tuple(std::move(x)...)); } // (f(A...) -> R) -> std::optional... -> std::optional // Apply a function to optional arguments if all are present. // N.B. If the function returns std::optional, MapOptional will return // std::optional> and you will probably want to // run it through JoinOptional to "squash" it. template std::optional MapOptional( std::function &&f, std::optional &&... x) { if (auto args{AllPresent(std::move(x)...)}) { return std::make_optional(std::apply(std::move(f), std::move(*args))); } return std::nullopt; } template std::optional MapOptional(R (*f)(A &&...), std::optional &&... x) { return MapOptional(std::function{f}, std::move(x)...); } // Given a VISITOR class of the general form // struct VISITOR { // using Result = ...; // using Types = std::tuple<...>; // template Result Test() { ... } // }; // SearchTypes will traverse the element types in the tuple in order // and invoke VISITOR::Test() on each until it returns a value that // casts to true. If no invocation of Test succeeds, SearchTypes will // return a default-constructed value VISITOR::Result{}. template common::IfNoLvalue SearchTypesHelper( VISITOR &&visitor) { using Tuple = typename VISITOR::Types; if constexpr (J < std::tuple_size_v) { if (auto result{visitor.template Test>()}) { return result; } return SearchTypesHelper(std::move(visitor)); } else { return typename VISITOR::Result{}; } } template common::IfNoLvalue SearchTypes( VISITOR &&visitor) { return SearchTypesHelper<0, VISITOR>(std::move(visitor)); } } #endif // FORTRAN_COMMON_TEMPLATE_H_