|
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 /* Macros to emulate C++11 typed enums and enum classes. */ |
|
8 |
|
9 #ifndef mozilla_TypedEnum_h |
|
10 #define mozilla_TypedEnum_h |
|
11 |
|
12 #include "mozilla/TypedEnumInternal.h" |
|
13 #include "mozilla/MacroArgs.h" |
|
14 |
|
15 #if defined(__cplusplus) |
|
16 |
|
17 /** |
|
18 * MOZ_ENUM_TYPE specifies the underlying numeric type for an enum. It's |
|
19 * specified by placing MOZ_ENUM_TYPE(type) immediately after the enum name in |
|
20 * its declaration, and before the opening curly brace, like |
|
21 * |
|
22 * enum MyEnum MOZ_ENUM_TYPE(uint16_t) |
|
23 * { |
|
24 * A, |
|
25 * B = 7, |
|
26 * C |
|
27 * }; |
|
28 * |
|
29 * In supporting compilers, the macro will expand to ": uint16_t". The |
|
30 * compiler will allocate exactly two bytes for MyEnum and will require all |
|
31 * enumerators to have values between 0 and 65535. (Thus specifying "B = |
|
32 * 100000" instead of "B = 7" would fail to compile.) In old compilers the |
|
33 * macro expands to the empty string, and the underlying type is generally |
|
34 * undefined. |
|
35 */ |
|
36 #ifdef MOZ_HAVE_CXX11_ENUM_TYPE |
|
37 # define MOZ_ENUM_TYPE(type) : type |
|
38 #else |
|
39 # define MOZ_ENUM_TYPE(type) /* no support */ |
|
40 #endif |
|
41 |
|
42 /** |
|
43 * MOZ_BEGIN_ENUM_CLASS and MOZ_END_ENUM_CLASS provide access to the |
|
44 * strongly-typed enumeration feature of C++11 ("enum class"). If supported |
|
45 * by the compiler, an enum defined using these macros will not be implicitly |
|
46 * converted to any other type, and its enumerators will be scoped using the |
|
47 * enumeration name. Place MOZ_BEGIN_ENUM_CLASS(EnumName [, type]) in place of |
|
48 * "enum EnumName {", and MOZ_END_ENUM_CLASS(EnumName) in place of the closing |
|
49 * "};". For example, |
|
50 * |
|
51 * MOZ_BEGIN_ENUM_CLASS(Enum, int32_t) |
|
52 * A, |
|
53 * B = 6 |
|
54 * MOZ_END_ENUM_CLASS(Enum) |
|
55 * |
|
56 * This will make "Enum::A" and "Enum::B" appear in the global scope, but "A" |
|
57 * and "B" will not. In compilers that support C++11 strongly-typed |
|
58 * enumerations, implicit conversions of Enum values to numeric types will |
|
59 * fail. In other compilers, Enum itself will actually be defined as a class, |
|
60 * and some implicit conversions will fail while others will succeed. |
|
61 * |
|
62 * The optional type argument specifies the underlying type for the enum where |
|
63 * supported, as with MOZ_ENUM_TYPE(). As with MOZ_ENUM_TYPE(), it will do |
|
64 * nothing on compilers that do not support it. |
|
65 * |
|
66 * MOZ_{BEGIN,END}_ENUM_CLASS doesn't work for defining enum classes nested |
|
67 * inside classes. To define an enum class nested inside another class, use |
|
68 * MOZ_{BEGIN,END}_NESTED_ENUM_CLASS, and place a MOZ_FINISH_NESTED_ENUM_CLASS |
|
69 * in namespace scope to handle bits that can only be implemented with |
|
70 * namespace-scoped code. For example: |
|
71 * |
|
72 * class FooBar { |
|
73 * |
|
74 * MOZ_BEGIN_NESTED_ENUM_CLASS(Enum, int32_t) |
|
75 * A, |
|
76 * B = 6 |
|
77 * MOZ_END_NESTED_ENUM_CLASS(Enum) |
|
78 * |
|
79 * }; |
|
80 * |
|
81 * MOZ_FINISH_NESTED_ENUM_CLASS(FooBar::Enum) |
|
82 */ |
|
83 #if defined(MOZ_HAVE_CXX11_STRONG_ENUMS) |
|
84 /* |
|
85 * All compilers that support strong enums also support an explicit |
|
86 * underlying type, so no extra check is needed. |
|
87 */ |
|
88 |
|
89 /* Single-argument form. */ |
|
90 # define MOZ_BEGIN_NESTED_ENUM_CLASS_HELPER1(Name) \ |
|
91 enum class Name { |
|
92 /* Two-argument form. */ |
|
93 # define MOZ_BEGIN_NESTED_ENUM_CLASS_HELPER2(Name, type) \ |
|
94 enum class Name : type { |
|
95 # define MOZ_END_NESTED_ENUM_CLASS(Name) \ |
|
96 }; |
|
97 # define MOZ_FINISH_NESTED_ENUM_CLASS(Name) /* nothing */ |
|
98 |
|
99 /* |
|
100 * MOZ_ENUM_CLASS_ENUM_TYPE allows using enum classes |
|
101 * as template parameter types. For that, we need integer types. |
|
102 * In the present case where the compiler supports strong enums, |
|
103 * these are already integer types so there is nothing more to do. |
|
104 */ |
|
105 # define MOZ_ENUM_CLASS_ENUM_TYPE(Name) Name |
|
106 /* |
|
107 * See the comment below about MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE. |
|
108 */ |
|
109 # define MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE(Name) Name |
|
110 #else |
|
111 /** |
|
112 * We need Name to both name a type, and scope the provided enumerator |
|
113 * names. Namespaces and classes both provide scoping, but namespaces |
|
114 * aren't types, so we need to use a class that wraps the enum values. We |
|
115 * have an implicit conversion from the inner enum type to the class, so |
|
116 * statements like |
|
117 * |
|
118 * Enum x = Enum::A; |
|
119 * |
|
120 * will still work. We need to define an implicit conversion from the class |
|
121 * to the inner enum as well, so that (for instance) switch statements will |
|
122 * work. This means that the class can be implicitly converted to a numeric |
|
123 * value as well via the enum type, since C++ allows an implicit |
|
124 * user-defined conversion followed by a standard conversion to still be |
|
125 * implicit. |
|
126 * |
|
127 * We have an explicit constructor from int defined, so that casts like |
|
128 * (Enum)7 will still work. We also have a zero-argument constructor with |
|
129 * no arguments, so declaration without initialization (like "Enum foo;") |
|
130 * will work. |
|
131 * |
|
132 * Additionally, we'll delete as many operators as possible for the inner |
|
133 * enum type, so statements like this will still fail: |
|
134 * |
|
135 * f(5 + Enum::B); // deleted operator+ |
|
136 * |
|
137 * But we can't prevent things like this, because C++ doesn't allow |
|
138 * overriding conversions or assignment operators for enums: |
|
139 * |
|
140 * int x = Enum::A; |
|
141 * int f() |
|
142 * { |
|
143 * return Enum::A; |
|
144 * } |
|
145 */ |
|
146 |
|
147 /* Single-argument form. */ |
|
148 # define MOZ_BEGIN_NESTED_ENUM_CLASS_HELPER1(Name) \ |
|
149 class Name \ |
|
150 { \ |
|
151 public: \ |
|
152 enum Enum \ |
|
153 { |
|
154 /* Two-argument form. */ |
|
155 # define MOZ_BEGIN_NESTED_ENUM_CLASS_HELPER2(Name, type) \ |
|
156 class Name \ |
|
157 { \ |
|
158 public: \ |
|
159 enum Enum MOZ_ENUM_TYPE(type) \ |
|
160 { |
|
161 # define MOZ_END_NESTED_ENUM_CLASS(Name) \ |
|
162 }; \ |
|
163 Name() {} \ |
|
164 MOZ_CONSTEXPR Name(Enum aEnum) : mEnum(aEnum) {} \ |
|
165 template<typename Other> \ |
|
166 explicit MOZ_CONSTEXPR Name(Other num) : mEnum((Enum)num) {} \ |
|
167 MOZ_CONSTEXPR operator Enum() const { return mEnum; } \ |
|
168 explicit MOZ_CONSTEXPR Name(const mozilla::CastableTypedEnumResult<Name>& aOther) \ |
|
169 : mEnum(aOther.get()) \ |
|
170 {} \ |
|
171 private: \ |
|
172 Enum mEnum; \ |
|
173 }; |
|
174 # define MOZ_FINISH_NESTED_ENUM_CLASS(Name) \ |
|
175 inline int operator+(const int&, const Name::Enum&) MOZ_DELETE; \ |
|
176 inline int operator+(const Name::Enum&, const int&) MOZ_DELETE; \ |
|
177 inline int operator-(const int&, const Name::Enum&) MOZ_DELETE; \ |
|
178 inline int operator-(const Name::Enum&, const int&) MOZ_DELETE; \ |
|
179 inline int operator*(const int&, const Name::Enum&) MOZ_DELETE; \ |
|
180 inline int operator*(const Name::Enum&, const int&) MOZ_DELETE; \ |
|
181 inline int operator/(const int&, const Name::Enum&) MOZ_DELETE; \ |
|
182 inline int operator/(const Name::Enum&, const int&) MOZ_DELETE; \ |
|
183 inline int operator%(const int&, const Name::Enum&) MOZ_DELETE; \ |
|
184 inline int operator%(const Name::Enum&, const int&) MOZ_DELETE; \ |
|
185 inline int operator+(const Name::Enum&) MOZ_DELETE; \ |
|
186 inline int operator-(const Name::Enum&) MOZ_DELETE; \ |
|
187 inline int& operator++(Name::Enum&) MOZ_DELETE; \ |
|
188 inline int operator++(Name::Enum&, int) MOZ_DELETE; \ |
|
189 inline int& operator--(Name::Enum&) MOZ_DELETE; \ |
|
190 inline int operator--(Name::Enum&, int) MOZ_DELETE; \ |
|
191 inline bool operator==(const int&, const Name::Enum&) MOZ_DELETE; \ |
|
192 inline bool operator==(const Name::Enum&, const int&) MOZ_DELETE; \ |
|
193 inline bool operator!=(const int&, const Name::Enum&) MOZ_DELETE; \ |
|
194 inline bool operator!=(const Name::Enum&, const int&) MOZ_DELETE; \ |
|
195 inline bool operator>(const int&, const Name::Enum&) MOZ_DELETE; \ |
|
196 inline bool operator>(const Name::Enum&, const int&) MOZ_DELETE; \ |
|
197 inline bool operator<(const int&, const Name::Enum&) MOZ_DELETE; \ |
|
198 inline bool operator<(const Name::Enum&, const int&) MOZ_DELETE; \ |
|
199 inline bool operator>=(const int&, const Name::Enum&) MOZ_DELETE; \ |
|
200 inline bool operator>=(const Name::Enum&, const int&) MOZ_DELETE; \ |
|
201 inline bool operator<=(const int&, const Name::Enum&) MOZ_DELETE; \ |
|
202 inline bool operator<=(const Name::Enum&, const int&) MOZ_DELETE; \ |
|
203 inline bool operator!(const Name::Enum&) MOZ_DELETE; \ |
|
204 inline bool operator&&(const bool&, const Name::Enum&) MOZ_DELETE; \ |
|
205 inline bool operator&&(const Name::Enum&, const bool&) MOZ_DELETE; \ |
|
206 inline bool operator||(const bool&, const Name::Enum&) MOZ_DELETE; \ |
|
207 inline bool operator||(const Name::Enum&, const bool&) MOZ_DELETE; \ |
|
208 inline int operator&(const int&, const Name::Enum&) MOZ_DELETE; \ |
|
209 inline int operator&(const Name::Enum&, const int&) MOZ_DELETE; \ |
|
210 inline int operator|(const int&, const Name::Enum&) MOZ_DELETE; \ |
|
211 inline int operator|(const Name::Enum&, const int&) MOZ_DELETE; \ |
|
212 inline int operator^(const int&, const Name::Enum&) MOZ_DELETE; \ |
|
213 inline int operator^(const Name::Enum&, const int&) MOZ_DELETE; \ |
|
214 inline int operator<<(const int&, const Name::Enum&) MOZ_DELETE; \ |
|
215 inline int operator<<(const Name::Enum&, const int&) MOZ_DELETE; \ |
|
216 inline int operator>>(const int&, const Name::Enum&) MOZ_DELETE; \ |
|
217 inline int operator>>(const Name::Enum&, const int&) MOZ_DELETE; \ |
|
218 inline int& operator+=(int&, const Name::Enum&) MOZ_DELETE; \ |
|
219 inline int& operator-=(int&, const Name::Enum&) MOZ_DELETE; \ |
|
220 inline int& operator*=(int&, const Name::Enum&) MOZ_DELETE; \ |
|
221 inline int& operator/=(int&, const Name::Enum&) MOZ_DELETE; \ |
|
222 inline int& operator%=(int&, const Name::Enum&) MOZ_DELETE; \ |
|
223 inline int& operator&=(int&, const Name::Enum&) MOZ_DELETE; \ |
|
224 inline int& operator|=(int&, const Name::Enum&) MOZ_DELETE; \ |
|
225 inline int& operator^=(int&, const Name::Enum&) MOZ_DELETE; \ |
|
226 inline int& operator<<=(int&, const Name::Enum&) MOZ_DELETE; \ |
|
227 inline int& operator>>=(int&, const Name::Enum&) MOZ_DELETE; |
|
228 |
|
229 /* |
|
230 * MOZ_ENUM_CLASS_ENUM_TYPE allows using enum classes |
|
231 * as template parameter types. For that, we need integer types. |
|
232 * In the present case, the integer type is the Enum nested type. |
|
233 */ |
|
234 # define MOZ_ENUM_CLASS_ENUM_TYPE(Name) Name::Enum |
|
235 /* |
|
236 * MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE is a variant of MOZ_ENUM_CLASS_ENUM_TYPE |
|
237 * to be used when the enum class at hand depends on template parameters. |
|
238 * |
|
239 * Indeed, if T depends on template parameters, in order to name a nested type |
|
240 * in T, C++ does not allow to just write "T::NestedType". Instead, we have |
|
241 * to write "typename T::NestedType". The role of this macro is to add |
|
242 * this "typename" keywords where needed. |
|
243 * |
|
244 * Example: |
|
245 * |
|
246 * template<typename T, MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE(T) Value> |
|
247 * struct S {}; |
|
248 * |
|
249 * MOZ_BEGIN_ENUM_CLASS(E) |
|
250 * Foo, |
|
251 * Bar |
|
252 * MOZ_END_ENUM_CLASS(E) |
|
253 * |
|
254 * S<E, E::Bar> s; |
|
255 * |
|
256 * In this example, the second template parameter to S is meant to be of type T, |
|
257 * but on non-C++11 compilers, type T is a class type, not an integer type, so |
|
258 * it is not accepted as the type of a constant template parameter. One would |
|
259 * then want to use MOZ_ENUM_CLASS_ENUM_TYPE(T), but that doesn't work either |
|
260 * as T depends on template parameters (more specifically here, T _is_ a template |
|
261 * parameter) so as MOZ_ENUM_CLASS_ENUM_TYPE(T) expands to T::Enum, we are missing |
|
262 * the required "typename" keyword. So here, MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE |
|
263 * is needed. |
|
264 */ |
|
265 # define MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE(Name) typename Name::Enum |
|
266 #endif |
|
267 |
|
268 # define MOZ_BEGIN_NESTED_ENUM_CLASS_GLUE(a, b) a b |
|
269 # define MOZ_BEGIN_NESTED_ENUM_CLASS(...) \ |
|
270 MOZ_BEGIN_NESTED_ENUM_CLASS_GLUE( \ |
|
271 MOZ_PASTE_PREFIX_AND_ARG_COUNT(MOZ_BEGIN_NESTED_ENUM_CLASS_HELPER, \ |
|
272 __VA_ARGS__), \ |
|
273 (__VA_ARGS__)) |
|
274 |
|
275 # define MOZ_BEGIN_ENUM_CLASS(...) MOZ_BEGIN_NESTED_ENUM_CLASS(__VA_ARGS__) |
|
276 # define MOZ_END_ENUM_CLASS(Name) \ |
|
277 MOZ_END_NESTED_ENUM_CLASS(Name) \ |
|
278 MOZ_FINISH_NESTED_ENUM_CLASS(Name) |
|
279 |
|
280 #endif /* __cplusplus */ |
|
281 |
|
282 #endif /* mozilla_TypedEnum_h */ |