// Copyright (c) 2018, 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_IDIOMS_H_ #define FORTRAN_COMMON_IDIOMS_H_ // Defines anything that might ever be useful in more than one source file // or that is too weird or too specific to the host C++ compiler to be // exposed elsewhere. #ifndef __cplusplus #error this is a C++ program #endif #if __cplusplus < 201703L #error this is a C++17 program #endif #if !__clang__ && defined __GNUC__ && __GNUC__ < 7 #error g++ >= 7.2 is required #endif #include #include #include #include #include #include #if __GNUC__ == 7 // Avoid a deduction bug in GNU 7.x headers by forcing the answer. namespace std { template struct is_trivially_copy_constructible> : false_type {}; template struct is_trivially_copy_constructible>> : false_type {}; } // namespace std #endif // enable "this is a std::string"s with the 's' suffix using namespace std::literals::string_literals; namespace Fortran::common { // Helper templates for combining a list of lambdas into an anonymous // struct for use with std::visit() on a std::variant<> sum type. // E.g.: std::visit(visitors{ // [&](const UnaryExpr &x) { ... }, // [&](const BinaryExpr &x) { ... }, // ... // }, structure.unionMember); template struct visitors : LAMBDAS... { using LAMBDAS::operator()...; }; template visitors(LAMBDAS... x)->visitors; // Calls std::fprintf(stderr, ...), then abort(). [[noreturn]] void die(const char *, ...); // For switch statements without default: labels. #define CRASH_NO_CASE \ Fortran::common::die("no case at " __FILE__ "(%d)", __LINE__) // For cheap assertions that should be applied in production. // To disable, compile with '-DCHECK=(void)' #ifndef CHECK #define CHECK(x) \ ((x) || \ (Fortran::common::die( \ "CHECK(" #x ") failed at " __FILE__ "(%d)", __LINE__), \ false)) #endif // User-defined type traits that default to false: // Invoke CLASS_TRAIT(traitName) to define a trait, then put // using traitName = std::true_type; (or false_type) // into the appropriate class definitions. You can then use // typename std::enable_if_t, ...> // in template specialization definitions. #define CLASS_TRAIT(T) \ namespace class_trait_ns_##T { \ template std::true_type test(typename A::T *); \ template std::false_type test(...); \ template \ constexpr bool has_trait{decltype(test(nullptr))::value}; \ template \ constexpr typename std::enable_if_t, bool> trait_value() { \ using U = typename A::T; \ return U::value; \ } \ template \ constexpr typename std::enable_if_t, bool> trait_value() { \ return false; \ } \ } \ template constexpr bool T { class_trait_ns_##T::trait_value() } // Define enum class NAME with the given enumerators, a static // function EnumToString() that maps enumerators to std::string, // and a constant NAME_enumSize that captures the number of items // in the enum class. std::string EnumIndexToString(int index, const char *names); template struct ListItemCount { constexpr ListItemCount(std::initializer_list list) : value{list.size()} {} const std::size_t value; }; #define ENUM_CLASS(NAME, ...) \ enum class NAME { __VA_ARGS__ }; \ static constexpr std::size_t NAME##_enumSize{[] { \ enum { __VA_ARGS__ }; \ return Fortran::common::ListItemCount{__VA_ARGS__}.value; \ }()}; \ static inline std::string EnumToString(NAME e) { \ return Fortran::common::EnumIndexToString( \ static_cast(e), #__VA_ARGS__); \ } // If a variant holds a value of a particular type, return a copy in a // std::optional<>. template std::optional GetIf(const VARIANT &u) { if (const A * x{std::get_if(&u)}) { return {*x}; } return std::nullopt; } // Collapses a nested std::optional> template std::optional JoinOptionals(std::optional> &&x) { if (x.has_value()) { return std::move(*x); } return std::nullopt; } // Apply a function to optional argument(s), if are all present. // N.B. This function uses a "functor" in the C++ sense -- a type with // a member function operator() -- to implement a "functor" in the category // theoretical sense. template std::optional MapOptional(std::function &f, std::optional &&x) { if (x.has_value()) { return {f(std::move(*x))}; } return std::nullopt; } template std::optional MapOptional(std::function &f, std::optional &&x, std::optional &&y) { if (x.has_value() && y.has_value()) { return {f(std::move(*x), std::move(*y))}; } return std::nullopt; } } // namespace Fortran::common #endif // FORTRAN_COMMON_IDIOMS_H_