1 // Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef FORTRAN_COMMON_IDIOMS_H_ 16 #define FORTRAN_COMMON_IDIOMS_H_ 17 18 // Defines anything that might ever be useful in more than one source file 19 // or that is too weird or too specific to the host C++ compiler to be 20 // exposed elsewhere. 21 22 #ifndef __cplusplus 23 #error this is a C++ program 24 #endif 25 #if __cplusplus < 201703L 26 #error this is a C++17 program 27 #endif 28 #if !__clang__ && defined __GNUC__ && __GNUC__ < 7 29 #error g++ >= 7.2 is required 30 #endif 31 32 #include <functional> 33 #include <list> 34 #include <optional> 35 #include <string> 36 #include <tuple> 37 #include <type_traits> 38 #include <variant> 39 40 #if __GNUC__ == 7 41 // Avoid a deduction bug in GNU 7.x headers by forcing the answer. 42 namespace std { 43 template<typename A> 44 struct is_trivially_copy_constructible<list<A>> : false_type {}; 45 template<typename A> 46 struct is_trivially_copy_constructible<optional<list<A>>> : false_type {}; 47 } 48 #endif 49 50 // enable "this is a std::string"s with the 's' suffix 51 using namespace std::literals::string_literals; 52 53 namespace Fortran::common { 54 55 // Helper templates for combining a list of lambdas into an anonymous 56 // struct for use with std::visit() on a std::variant<> sum type. 57 // E.g.: std::visit(visitors{ 58 // [&](const firstType &x) { ... }, 59 // [&](const secondType &x) { ... }, 60 // ... 61 // [&](const auto &catchAll) { ... }}, variantObject); 62 63 template<typename... LAMBDAS> struct visitors : LAMBDAS... { 64 using LAMBDAS::operator()...; 65 }; 66 67 template<typename... LAMBDAS> visitors(LAMBDAS... x)->visitors<LAMBDAS...>; 68 69 // Calls std::fprintf(stderr, ...), then abort(). 70 [[noreturn]] void die(const char *, ...); 71 72 #define DIE(x) Fortran::common::die(x " at " __FILE__ "(%d)", __LINE__) 73 74 // For switch statement default: labels. 75 #define CRASH_NO_CASE DIE("no case") 76 77 // clang-format off 78 // For switch statements whose cases have return statements for 79 // all possibilities. Clang emits warnings if the default: is 80 // present, gcc emits warnings if it is absent. 81 #if __clang__ 82 #define SWITCH_COVERS_ALL_CASES 83 #else 84 #define SWITCH_COVERS_ALL_CASES default: CRASH_NO_CASE; 85 #endif 86 // clang-format on 87 88 // For cheap assertions that should be applied in production. 89 // To disable, compile with '-DCHECK=(void)' 90 #ifndef CHECK 91 #define CHECK(x) ((x) || (DIE("CHECK(" #x ") failed"), false)) 92 #endif 93 94 // User-defined type traits that default to false: 95 // Invoke CLASS_TRAIT(traitName) to define a trait, then put 96 // using traitName = std::true_type; (or false_type) 97 // into the appropriate class definitions. You can then use 98 // typename std::enable_if_t<traitName<...>, ...> 99 // in template specialization definitions. 100 #define CLASS_TRAIT(T) \ 101 namespace class_trait_ns_##T { \ 102 template<typename A> std::true_type test(typename A::T *); \ 103 template<typename A> std::false_type test(...); \ 104 template<typename A> \ 105 constexpr bool has_trait{decltype(test<A>(nullptr))::value}; \ 106 template<typename A> constexpr bool trait_value() { \ 107 if constexpr (has_trait<A>) { \ 108 using U = typename A::T; \ 109 return U::value; \ 110 } else { \ 111 return false; \ 112 } \ 113 } \ 114 } \ 115 template<typename A> constexpr bool T{class_trait_ns_##T::trait_value<A>()}; 116 117 #if !defined ATTRIBUTE_UNUSED && (__clang__ || __GNUC__) 118 #define ATTRIBUTE_UNUSED __attribute__((unused)) 119 #endif 120 121 // Define enum class NAME with the given enumerators, a static 122 // function EnumToString() that maps enumerators to std::string, 123 // and a constant NAME_enumSize that captures the number of items 124 // in the enum class. 125 126 std::string EnumIndexToString(int index, const char *names); 127 128 template<typename A> struct ListItemCount { 129 constexpr ListItemCount(std::initializer_list<A> list) : value{list.size()} {} 130 const std::size_t value; 131 }; 132 133 #define ENUM_CLASS(NAME, ...) \ 134 enum class NAME { __VA_ARGS__ }; \ 135 ATTRIBUTE_UNUSED static constexpr std::size_t NAME##_enumSize{[] { \ 136 enum { __VA_ARGS__ }; \ 137 return Fortran::common::ListItemCount{__VA_ARGS__}.value; \ 138 }()}; \ 139 ATTRIBUTE_UNUSED static inline std::string EnumToString(NAME e) { \ 140 return Fortran::common::EnumIndexToString( \ 141 static_cast<int>(e), #__VA_ARGS__); \ 142 } 143 144 // Check that a pointer is non-null and dereference it 145 #define DEREF(p) Fortran::common::Deref(p, __FILE__, __LINE__) 146 147 template<typename T> T &Deref(T *p, const char *file, int line) { 148 if (p == nullptr) { 149 Fortran::common::die("nullptr dereference at %s(%d)", file, line); 150 } 151 return *p; 152 } 153 154 // Given a const reference to a value, return a copy of the value. 155 template<typename A> A Clone(const A &x) { return x; } 156 157 // C++ does a weird and dangerous thing when deducing template type parameters 158 // from function arguments: lvalue references are allowed to match rvalue 159 // reference arguments. Template function declarations like 160 // template<typename A> int foo(A &&); 161 // need to be protected against this C++ language feature when functions 162 // may modify such arguments. Use these type functions to invoke SFINAE 163 // on a result type via 164 // template<typename A> common::IfNoLvalue<int, A> foo(A &&); 165 // or, for constructors, 166 // template<typename A, typename = common::NoLvalue<A>> int foo(A &&); 167 // This works with parameter packs too. 168 template<typename A, typename... B> 169 using IfNoLvalue = std::enable_if_t<(... && !std::is_lvalue_reference_v<B>), A>; 170 template<typename... RVREF> using NoLvalue = IfNoLvalue<void, RVREF...>; 171 } 172 #endif // FORTRAN_COMMON_IDIOMS_H_ 173