DefineEnum.h (10920B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* Poor man's reflection for enumerations. */ 8 9 #ifndef mozilla_DefineEnum_h 10 #define mozilla_DefineEnum_h 11 12 #include <stddef.h> // for size_t 13 #include <ostream> // for std::ostream 14 15 #include "mozilla/MacroArgs.h" // for MOZ_ARG_COUNT 16 #include "mozilla/MacroForEach.h" // for MOZ_FOR_EACH 17 18 /** 19 * MOZ_UNWRAP_ARGS is a helper macro that unwraps a list of comma-separated 20 * items enclosed in parentheses, to yield just the items. 21 * 22 * Usage: |MOZ_UNWRAP_ARGS foo| (note the absence of parentheses in the 23 * invocation), where |foo| is a parenthesis-enclosed list. 24 * For exampe if |foo| is |(3, 4, 5)|, then the expansion is just |3, 4, 5|. 25 */ 26 #define MOZ_UNWRAP_ARGS(...) __VA_ARGS__ 27 28 /** 29 * MOZ_DEFINE_ENUM(aEnumName, aEnumerators) is a macro that allows 30 * simultaneously defining an enumeration named |aEnumName|, and a constant 31 * that stores the number of enumerators it has. 32 * 33 * The motivation is to allow the enumeration to evolve over time without 34 * either having to manually keep such a constant up to date, or having to 35 * add a special "sentinel" enumerator for this purpose. (While adding a 36 * "sentinel" enumerator is trivial, it causes headaches with "switch" 37 * statements. We often try to write "switch" statements whose cases exhaust 38 * the enumerators and don't have a "default" case, so that if a new 39 * enumerator is added and we forget to handle it in the "switch", the 40 * compiler points it out. But this means we need to explicitly handle the 41 * sentinel in every "switch".) 42 * 43 * |aEnumerators| is expected to be a comma-separated list of enumerators, 44 * enclosed in parentheses. The enumerators may NOT have associated 45 * initializers (an attempt to have one will result in a compiler error). 46 * This ensures that the enumerator values are in the range [0, N), where N 47 * is the number of enumerators. 48 * 49 * The list of enumerators cannot contain a trailing comma. This is a 50 * limitation of MOZ_FOR_EACH, which we use in the implementation; if 51 * MOZ_FOR_EACH supported trailing commas, we could too. 52 * 53 * The generated constant has the name "k" + |aEnumName| + "Count", and type 54 * "size_t". The enumeration and the constant are both defined in the scope 55 * in which the macro is invoked. 56 * 57 * For convenience, a constant of the enumeration type named 58 * "kHighest" + |aEnumName| is also defined, whose value is the highest 59 * valid enumerator, assuming the enumerators have contiguous values starting 60 * from 0. 61 * 62 * Invocation of the macro may be followed by a semicolon, if one prefers a 63 * more declaration-like syntax. 64 * 65 * Example invocation: 66 * MOZ_DEFINE_ENUM(MyEnum, (Foo, Bar, Baz)); 67 * 68 * This expands to: 69 * enum MyEnum { Foo, Bar, Baz }; 70 * constexpr size_t kMyEnumCount = 3; 71 * constexpr MyEnum kHighestMyEnum = MyEnum(kMyEnumCount - 1); 72 * // some static_asserts to ensure the values are in the range [0, 3) 73 * 74 * The macro also has several variants: 75 * 76 * - A |_CLASS| variant, which generates an |enum class| instead of 77 * a plain enum. 78 * 79 * - A |_WITH_BASE| variant which generates an enum with a specified 80 * underlying ("base") type, which is provided as an additional 81 * argument in second position. 82 * 83 * - An |_AT_CLASS_SCOPE| variant, designed for enumerations defined 84 * at class scope. For these, the generated constants are static, 85 * and have names prefixed with "s" instead of "k" as per 86 * naming convention. 87 * 88 * - A |_TOSTRING| variant, which generates an EnumValueToString function, 89 * converting the enum items to strings, and implements an "operator<<", 90 * providing a consistent way to convert enums to strings by 91 * mozilla::ToString function regardless of their definition context. 92 * For users needing C-string compatibility for logging in restricted 93 * contexts or performance sensitive applications, EnumValueToString is 94 * preferred. 95 * 96 * (and combinations of these). 97 */ 98 99 /* 100 * A helper macro for asserting that an enumerator does not have an initializer. 101 * 102 * The static_assert and the comparison are just scaffolding; the important 103 * part is forming the expression |aEnumName::aEnumeratorDecl|. 104 * 105 * If |aEnumeratorDecl| is just the enumerator name without an identifier, 106 * this expression compiles fine. However, if |aEnumeratorDecl| includes an 107 * initializer, as in |eEnumerator = initializer|, then this will fail to 108 * compile in expression context, since |eEnumerator| is not an lvalue. 109 * 110 * (The static_assert itself should always pass in the absence of the above 111 * error, since turning on a bit can only increase an integer value. It just 112 * provides a place to put the expression we want to form.) 113 */ 114 115 #define MOZ_ASSERT_ENUMERATOR_HAS_NO_INITIALIZER(aEnumName, aEnumeratorDecl) \ 116 static_assert( \ 117 int(aEnumName::aEnumeratorDecl) <= \ 118 (int(aEnumName::aEnumeratorDecl) | 1), \ 119 "MOZ_DEFINE_ENUM does not allow enumerators to have initializers"); 120 121 #define MOZ_DEFINE_ENUM_IMPL(aEnumName, aClassSpec, aBaseSpec, aEnumerators) \ 122 enum aClassSpec aEnumName aBaseSpec{MOZ_UNWRAP_ARGS aEnumerators}; \ 123 constexpr size_t k##aEnumName##Count = MOZ_ARG_COUNT aEnumerators; \ 124 constexpr aEnumName kHighest##aEnumName = \ 125 aEnumName(k##aEnumName##Count - 1); \ 126 MOZ_FOR_EACH(MOZ_ASSERT_ENUMERATOR_HAS_NO_INITIALIZER, (aEnumName, ), \ 127 aEnumerators) 128 129 #define MOZ_DEFINE_ENUM(aEnumName, aEnumerators) \ 130 MOZ_DEFINE_ENUM_IMPL(aEnumName, , , aEnumerators) 131 132 #define MOZ_DEFINE_ENUM_WITH_BASE(aEnumName, aBaseName, aEnumerators) \ 133 MOZ_DEFINE_ENUM_IMPL(aEnumName, , : aBaseName, aEnumerators) 134 135 #define MOZ_DEFINE_ENUM_CLASS(aEnumName, aEnumerators) \ 136 MOZ_DEFINE_ENUM_IMPL(aEnumName, class, , aEnumerators) 137 138 #define MOZ_DEFINE_ENUM_CLASS_WITH_BASE(aEnumName, aBaseName, aEnumerators) \ 139 MOZ_DEFINE_ENUM_IMPL(aEnumName, class, : aBaseName, aEnumerators) 140 141 #define MOZ_DEFINE_ENUM_AT_CLASS_SCOPE_IMPL(aEnumName, aClassSpec, aBaseSpec, \ 142 aEnumerators) \ 143 enum aClassSpec aEnumName aBaseSpec{MOZ_UNWRAP_ARGS aEnumerators}; \ 144 constexpr static size_t s##aEnumName##Count = MOZ_ARG_COUNT aEnumerators; \ 145 constexpr static aEnumName sHighest##aEnumName = \ 146 aEnumName(s##aEnumName##Count - 1); \ 147 MOZ_FOR_EACH(MOZ_ASSERT_ENUMERATOR_HAS_NO_INITIALIZER, (aEnumName, ), \ 148 aEnumerators) 149 150 #define MOZ_DEFINE_ENUM_AT_CLASS_SCOPE(aEnumName, aEnumerators) \ 151 MOZ_DEFINE_ENUM_AT_CLASS_SCOPE_IMPL(aEnumName, , , aEnumerators) 152 153 #define MOZ_DEFINE_ENUM_WITH_BASE_AT_CLASS_SCOPE(aEnumName, aBaseName, \ 154 aEnumerators) \ 155 MOZ_DEFINE_ENUM_AT_CLASS_SCOPE_IMPL(aEnumName, , : aBaseName, aEnumerators) 156 157 #define MOZ_DEFINE_ENUM_CLASS_AT_CLASS_SCOPE(aEnumName, aEnumerators) \ 158 MOZ_DEFINE_ENUM_AT_CLASS_SCOPE_IMPL(aEnumName, class, , aEnumerators) 159 160 #define MOZ_DEFINE_ENUM_CLASS_WITH_BASE_AT_CLASS_SCOPE(aEnumName, aBaseName, \ 161 aEnumerators) \ 162 MOZ_DEFINE_ENUM_AT_CLASS_SCOPE_IMPL(aEnumName, class, : aBaseName, \ 163 aEnumerators) 164 165 #define MOZ_DEFINE_ENUM_TO_ENUM_TEXT(aEnumeratorDecl) #aEnumeratorDecl 166 167 #define MOZ_DEFINE_ENUM_TOSTRING_FUNC_IMPL(aEnumName, aEnumerators, aFriend) \ 168 inline static const char* EnumValueToString(const aEnumName& aEnum) { \ 169 static constexpr const char* kMappedStrings[] = {MOZ_FOR_EACH_SEPARATED( \ 170 MOZ_DEFINE_ENUM_TO_ENUM_TEXT, (, ), (), aEnumerators)}; \ 171 return kMappedStrings[static_cast<size_t>(aEnum)]; \ 172 } \ 173 aFriend inline std::ostream& operator<<(std::ostream& aStream, \ 174 const aEnumName& aEnum) { \ 175 aStream << EnumValueToString(aEnum); \ 176 return aStream; \ 177 } 178 179 #define MOZ_DEFINE_ENUM_TOSTRING_FUNC(aEnumName, aEnumerators) \ 180 MOZ_DEFINE_ENUM_TOSTRING_FUNC_IMPL(aEnumName, aEnumerators, ) 181 182 #define MOZ_DEFINE_ENUM_TOSTRING_FUNC_IN_CLASS(aEnumName, aEnumerators) \ 183 MOZ_DEFINE_ENUM_TOSTRING_FUNC_IMPL(aEnumName, aEnumerators, friend) 184 185 #define MOZ_DEFINE_ENUM_WITH_BASE_AND_TOSTRING(aEnumName, aBaseName, \ 186 aEnumerators) \ 187 MOZ_DEFINE_ENUM_WITH_BASE(aEnumName, aBaseName, aEnumerators) \ 188 MOZ_DEFINE_ENUM_TOSTRING_FUNC(aEnumName, aEnumerators) 189 190 #define MOZ_DEFINE_ENUM_CLASS_WITH_TOSTRING(aEnumName, aEnumerators) \ 191 MOZ_DEFINE_ENUM_CLASS(aEnumName, aEnumerators) \ 192 MOZ_DEFINE_ENUM_TOSTRING_FUNC(aEnumName, aEnumerators) 193 194 #define MOZ_DEFINE_ENUM_CLASS_WITH_BASE_AND_TOSTRING(aEnumName, aBaseName, \ 195 aEnumerators) \ 196 MOZ_DEFINE_ENUM_CLASS_WITH_BASE(aEnumName, aBaseName, aEnumerators) \ 197 MOZ_DEFINE_ENUM_TOSTRING_FUNC(aEnumName, aEnumerators) 198 199 #define MOZ_DEFINE_ENUM_WITH_TOSTRING_AT_CLASS_SCOPE(aEnumName, aEnumerators) \ 200 MOZ_DEFINE_ENUM_AT_CLASS_SCOPE(aEnumName, aEnumerators) \ 201 MOZ_DEFINE_ENUM_TOSTRING_FUNC_IN_CLASS(aEnumName, aEnumerators) 202 203 #define MOZ_DEFINE_ENUM_WITH_BASE_AND_TOSTRING_AT_CLASS_SCOPE( \ 204 aEnumName, aBaseName, aEnumerators) \ 205 MOZ_DEFINE_ENUM_WITH_BASE_AT_CLASS_SCOPE(aEnumName, aBaseName, aEnumerators) \ 206 MOZ_DEFINE_ENUM_TOSTRING_FUNC_IN_CLASS(aEnumName, aEnumerators) 207 208 #define MOZ_DEFINE_ENUM_CLASS_WITH_TOSTRING_AT_CLASS_SCOPE(aEnumName, \ 209 aEnumerators) \ 210 MOZ_DEFINE_ENUM_CLASS_AT_CLASS_SCOPE(aEnumName, aEnumerators) \ 211 MOZ_DEFINE_ENUM_TOSTRING_FUNC_IN_CLASS(aEnumName, aEnumerators) 212 213 #define MOZ_DEFINE_ENUM_CLASS_WITH_BASE_AND_TOSTRING_AT_CLASS_SCOPE( \ 214 aEnumName, aBaseName, aEnumerators) \ 215 MOZ_DEFINE_ENUM_CLASS_WITH_BASE_AT_CLASS_SCOPE(aEnumName, aBaseName, \ 216 aEnumerators) \ 217 MOZ_DEFINE_ENUM_TOSTRING_FUNC_IN_CLASS(aEnumName, aEnumerators) 218 219 #endif // mozilla_DefineEnum_h