1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mfbt/TypedEnum.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,282 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* Macros to emulate C++11 typed enums and enum classes. */ 1.11 + 1.12 +#ifndef mozilla_TypedEnum_h 1.13 +#define mozilla_TypedEnum_h 1.14 + 1.15 +#include "mozilla/TypedEnumInternal.h" 1.16 +#include "mozilla/MacroArgs.h" 1.17 + 1.18 +#if defined(__cplusplus) 1.19 + 1.20 +/** 1.21 + * MOZ_ENUM_TYPE specifies the underlying numeric type for an enum. It's 1.22 + * specified by placing MOZ_ENUM_TYPE(type) immediately after the enum name in 1.23 + * its declaration, and before the opening curly brace, like 1.24 + * 1.25 + * enum MyEnum MOZ_ENUM_TYPE(uint16_t) 1.26 + * { 1.27 + * A, 1.28 + * B = 7, 1.29 + * C 1.30 + * }; 1.31 + * 1.32 + * In supporting compilers, the macro will expand to ": uint16_t". The 1.33 + * compiler will allocate exactly two bytes for MyEnum and will require all 1.34 + * enumerators to have values between 0 and 65535. (Thus specifying "B = 1.35 + * 100000" instead of "B = 7" would fail to compile.) In old compilers the 1.36 + * macro expands to the empty string, and the underlying type is generally 1.37 + * undefined. 1.38 + */ 1.39 +#ifdef MOZ_HAVE_CXX11_ENUM_TYPE 1.40 +# define MOZ_ENUM_TYPE(type) : type 1.41 +#else 1.42 +# define MOZ_ENUM_TYPE(type) /* no support */ 1.43 +#endif 1.44 + 1.45 +/** 1.46 + * MOZ_BEGIN_ENUM_CLASS and MOZ_END_ENUM_CLASS provide access to the 1.47 + * strongly-typed enumeration feature of C++11 ("enum class"). If supported 1.48 + * by the compiler, an enum defined using these macros will not be implicitly 1.49 + * converted to any other type, and its enumerators will be scoped using the 1.50 + * enumeration name. Place MOZ_BEGIN_ENUM_CLASS(EnumName [, type]) in place of 1.51 + * "enum EnumName {", and MOZ_END_ENUM_CLASS(EnumName) in place of the closing 1.52 + * "};". For example, 1.53 + * 1.54 + * MOZ_BEGIN_ENUM_CLASS(Enum, int32_t) 1.55 + * A, 1.56 + * B = 6 1.57 + * MOZ_END_ENUM_CLASS(Enum) 1.58 + * 1.59 + * This will make "Enum::A" and "Enum::B" appear in the global scope, but "A" 1.60 + * and "B" will not. In compilers that support C++11 strongly-typed 1.61 + * enumerations, implicit conversions of Enum values to numeric types will 1.62 + * fail. In other compilers, Enum itself will actually be defined as a class, 1.63 + * and some implicit conversions will fail while others will succeed. 1.64 + * 1.65 + * The optional type argument specifies the underlying type for the enum where 1.66 + * supported, as with MOZ_ENUM_TYPE(). As with MOZ_ENUM_TYPE(), it will do 1.67 + * nothing on compilers that do not support it. 1.68 + * 1.69 + * MOZ_{BEGIN,END}_ENUM_CLASS doesn't work for defining enum classes nested 1.70 + * inside classes. To define an enum class nested inside another class, use 1.71 + * MOZ_{BEGIN,END}_NESTED_ENUM_CLASS, and place a MOZ_FINISH_NESTED_ENUM_CLASS 1.72 + * in namespace scope to handle bits that can only be implemented with 1.73 + * namespace-scoped code. For example: 1.74 + * 1.75 + * class FooBar { 1.76 + * 1.77 + * MOZ_BEGIN_NESTED_ENUM_CLASS(Enum, int32_t) 1.78 + * A, 1.79 + * B = 6 1.80 + * MOZ_END_NESTED_ENUM_CLASS(Enum) 1.81 + * 1.82 + * }; 1.83 + * 1.84 + * MOZ_FINISH_NESTED_ENUM_CLASS(FooBar::Enum) 1.85 + */ 1.86 +#if defined(MOZ_HAVE_CXX11_STRONG_ENUMS) 1.87 + /* 1.88 + * All compilers that support strong enums also support an explicit 1.89 + * underlying type, so no extra check is needed. 1.90 + */ 1.91 + 1.92 + /* Single-argument form. */ 1.93 +# define MOZ_BEGIN_NESTED_ENUM_CLASS_HELPER1(Name) \ 1.94 + enum class Name { 1.95 + /* Two-argument form. */ 1.96 +# define MOZ_BEGIN_NESTED_ENUM_CLASS_HELPER2(Name, type) \ 1.97 + enum class Name : type { 1.98 +# define MOZ_END_NESTED_ENUM_CLASS(Name) \ 1.99 + }; 1.100 +# define MOZ_FINISH_NESTED_ENUM_CLASS(Name) /* nothing */ 1.101 + 1.102 + /* 1.103 + * MOZ_ENUM_CLASS_ENUM_TYPE allows using enum classes 1.104 + * as template parameter types. For that, we need integer types. 1.105 + * In the present case where the compiler supports strong enums, 1.106 + * these are already integer types so there is nothing more to do. 1.107 + */ 1.108 +# define MOZ_ENUM_CLASS_ENUM_TYPE(Name) Name 1.109 + /* 1.110 + * See the comment below about MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE. 1.111 + */ 1.112 +# define MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE(Name) Name 1.113 +#else 1.114 + /** 1.115 + * We need Name to both name a type, and scope the provided enumerator 1.116 + * names. Namespaces and classes both provide scoping, but namespaces 1.117 + * aren't types, so we need to use a class that wraps the enum values. We 1.118 + * have an implicit conversion from the inner enum type to the class, so 1.119 + * statements like 1.120 + * 1.121 + * Enum x = Enum::A; 1.122 + * 1.123 + * will still work. We need to define an implicit conversion from the class 1.124 + * to the inner enum as well, so that (for instance) switch statements will 1.125 + * work. This means that the class can be implicitly converted to a numeric 1.126 + * value as well via the enum type, since C++ allows an implicit 1.127 + * user-defined conversion followed by a standard conversion to still be 1.128 + * implicit. 1.129 + * 1.130 + * We have an explicit constructor from int defined, so that casts like 1.131 + * (Enum)7 will still work. We also have a zero-argument constructor with 1.132 + * no arguments, so declaration without initialization (like "Enum foo;") 1.133 + * will work. 1.134 + * 1.135 + * Additionally, we'll delete as many operators as possible for the inner 1.136 + * enum type, so statements like this will still fail: 1.137 + * 1.138 + * f(5 + Enum::B); // deleted operator+ 1.139 + * 1.140 + * But we can't prevent things like this, because C++ doesn't allow 1.141 + * overriding conversions or assignment operators for enums: 1.142 + * 1.143 + * int x = Enum::A; 1.144 + * int f() 1.145 + * { 1.146 + * return Enum::A; 1.147 + * } 1.148 + */ 1.149 + 1.150 + /* Single-argument form. */ 1.151 +# define MOZ_BEGIN_NESTED_ENUM_CLASS_HELPER1(Name) \ 1.152 + class Name \ 1.153 + { \ 1.154 + public: \ 1.155 + enum Enum \ 1.156 + { 1.157 + /* Two-argument form. */ 1.158 +# define MOZ_BEGIN_NESTED_ENUM_CLASS_HELPER2(Name, type) \ 1.159 + class Name \ 1.160 + { \ 1.161 + public: \ 1.162 + enum Enum MOZ_ENUM_TYPE(type) \ 1.163 + { 1.164 +# define MOZ_END_NESTED_ENUM_CLASS(Name) \ 1.165 + }; \ 1.166 + Name() {} \ 1.167 + MOZ_CONSTEXPR Name(Enum aEnum) : mEnum(aEnum) {} \ 1.168 + template<typename Other> \ 1.169 + explicit MOZ_CONSTEXPR Name(Other num) : mEnum((Enum)num) {} \ 1.170 + MOZ_CONSTEXPR operator Enum() const { return mEnum; } \ 1.171 + explicit MOZ_CONSTEXPR Name(const mozilla::CastableTypedEnumResult<Name>& aOther) \ 1.172 + : mEnum(aOther.get()) \ 1.173 + {} \ 1.174 + private: \ 1.175 + Enum mEnum; \ 1.176 + }; 1.177 +# define MOZ_FINISH_NESTED_ENUM_CLASS(Name) \ 1.178 + inline int operator+(const int&, const Name::Enum&) MOZ_DELETE; \ 1.179 + inline int operator+(const Name::Enum&, const int&) MOZ_DELETE; \ 1.180 + inline int operator-(const int&, const Name::Enum&) MOZ_DELETE; \ 1.181 + inline int operator-(const Name::Enum&, const int&) MOZ_DELETE; \ 1.182 + inline int operator*(const int&, const Name::Enum&) MOZ_DELETE; \ 1.183 + inline int operator*(const Name::Enum&, const int&) MOZ_DELETE; \ 1.184 + inline int operator/(const int&, const Name::Enum&) MOZ_DELETE; \ 1.185 + inline int operator/(const Name::Enum&, const int&) MOZ_DELETE; \ 1.186 + inline int operator%(const int&, const Name::Enum&) MOZ_DELETE; \ 1.187 + inline int operator%(const Name::Enum&, const int&) MOZ_DELETE; \ 1.188 + inline int operator+(const Name::Enum&) MOZ_DELETE; \ 1.189 + inline int operator-(const Name::Enum&) MOZ_DELETE; \ 1.190 + inline int& operator++(Name::Enum&) MOZ_DELETE; \ 1.191 + inline int operator++(Name::Enum&, int) MOZ_DELETE; \ 1.192 + inline int& operator--(Name::Enum&) MOZ_DELETE; \ 1.193 + inline int operator--(Name::Enum&, int) MOZ_DELETE; \ 1.194 + inline bool operator==(const int&, const Name::Enum&) MOZ_DELETE; \ 1.195 + inline bool operator==(const Name::Enum&, const int&) MOZ_DELETE; \ 1.196 + inline bool operator!=(const int&, const Name::Enum&) MOZ_DELETE; \ 1.197 + inline bool operator!=(const Name::Enum&, const int&) MOZ_DELETE; \ 1.198 + inline bool operator>(const int&, const Name::Enum&) MOZ_DELETE; \ 1.199 + inline bool operator>(const Name::Enum&, const int&) MOZ_DELETE; \ 1.200 + inline bool operator<(const int&, const Name::Enum&) MOZ_DELETE; \ 1.201 + inline bool operator<(const Name::Enum&, const int&) MOZ_DELETE; \ 1.202 + inline bool operator>=(const int&, const Name::Enum&) MOZ_DELETE; \ 1.203 + inline bool operator>=(const Name::Enum&, const int&) MOZ_DELETE; \ 1.204 + inline bool operator<=(const int&, const Name::Enum&) MOZ_DELETE; \ 1.205 + inline bool operator<=(const Name::Enum&, const int&) MOZ_DELETE; \ 1.206 + inline bool operator!(const Name::Enum&) MOZ_DELETE; \ 1.207 + inline bool operator&&(const bool&, const Name::Enum&) MOZ_DELETE; \ 1.208 + inline bool operator&&(const Name::Enum&, const bool&) MOZ_DELETE; \ 1.209 + inline bool operator||(const bool&, const Name::Enum&) MOZ_DELETE; \ 1.210 + inline bool operator||(const Name::Enum&, const bool&) MOZ_DELETE; \ 1.211 + inline int operator&(const int&, const Name::Enum&) MOZ_DELETE; \ 1.212 + inline int operator&(const Name::Enum&, const int&) MOZ_DELETE; \ 1.213 + inline int operator|(const int&, const Name::Enum&) MOZ_DELETE; \ 1.214 + inline int operator|(const Name::Enum&, const int&) MOZ_DELETE; \ 1.215 + inline int operator^(const int&, const Name::Enum&) MOZ_DELETE; \ 1.216 + inline int operator^(const Name::Enum&, const int&) MOZ_DELETE; \ 1.217 + inline int operator<<(const int&, const Name::Enum&) MOZ_DELETE; \ 1.218 + inline int operator<<(const Name::Enum&, const int&) MOZ_DELETE; \ 1.219 + inline int operator>>(const int&, const Name::Enum&) MOZ_DELETE; \ 1.220 + inline int operator>>(const Name::Enum&, const int&) MOZ_DELETE; \ 1.221 + inline int& operator+=(int&, const Name::Enum&) MOZ_DELETE; \ 1.222 + inline int& operator-=(int&, const Name::Enum&) MOZ_DELETE; \ 1.223 + inline int& operator*=(int&, const Name::Enum&) MOZ_DELETE; \ 1.224 + inline int& operator/=(int&, const Name::Enum&) MOZ_DELETE; \ 1.225 + inline int& operator%=(int&, const Name::Enum&) MOZ_DELETE; \ 1.226 + inline int& operator&=(int&, const Name::Enum&) MOZ_DELETE; \ 1.227 + inline int& operator|=(int&, const Name::Enum&) MOZ_DELETE; \ 1.228 + inline int& operator^=(int&, const Name::Enum&) MOZ_DELETE; \ 1.229 + inline int& operator<<=(int&, const Name::Enum&) MOZ_DELETE; \ 1.230 + inline int& operator>>=(int&, const Name::Enum&) MOZ_DELETE; 1.231 + 1.232 + /* 1.233 + * MOZ_ENUM_CLASS_ENUM_TYPE allows using enum classes 1.234 + * as template parameter types. For that, we need integer types. 1.235 + * In the present case, the integer type is the Enum nested type. 1.236 + */ 1.237 +# define MOZ_ENUM_CLASS_ENUM_TYPE(Name) Name::Enum 1.238 + /* 1.239 + * MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE is a variant of MOZ_ENUM_CLASS_ENUM_TYPE 1.240 + * to be used when the enum class at hand depends on template parameters. 1.241 + * 1.242 + * Indeed, if T depends on template parameters, in order to name a nested type 1.243 + * in T, C++ does not allow to just write "T::NestedType". Instead, we have 1.244 + * to write "typename T::NestedType". The role of this macro is to add 1.245 + * this "typename" keywords where needed. 1.246 + * 1.247 + * Example: 1.248 + * 1.249 + * template<typename T, MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE(T) Value> 1.250 + * struct S {}; 1.251 + * 1.252 + * MOZ_BEGIN_ENUM_CLASS(E) 1.253 + * Foo, 1.254 + * Bar 1.255 + * MOZ_END_ENUM_CLASS(E) 1.256 + * 1.257 + * S<E, E::Bar> s; 1.258 + * 1.259 + * In this example, the second template parameter to S is meant to be of type T, 1.260 + * but on non-C++11 compilers, type T is a class type, not an integer type, so 1.261 + * it is not accepted as the type of a constant template parameter. One would 1.262 + * then want to use MOZ_ENUM_CLASS_ENUM_TYPE(T), but that doesn't work either 1.263 + * as T depends on template parameters (more specifically here, T _is_ a template 1.264 + * parameter) so as MOZ_ENUM_CLASS_ENUM_TYPE(T) expands to T::Enum, we are missing 1.265 + * the required "typename" keyword. So here, MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE 1.266 + * is needed. 1.267 + */ 1.268 +# define MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE(Name) typename Name::Enum 1.269 +#endif 1.270 + 1.271 +# define MOZ_BEGIN_NESTED_ENUM_CLASS_GLUE(a, b) a b 1.272 +# define MOZ_BEGIN_NESTED_ENUM_CLASS(...) \ 1.273 + MOZ_BEGIN_NESTED_ENUM_CLASS_GLUE( \ 1.274 + MOZ_PASTE_PREFIX_AND_ARG_COUNT(MOZ_BEGIN_NESTED_ENUM_CLASS_HELPER, \ 1.275 + __VA_ARGS__), \ 1.276 + (__VA_ARGS__)) 1.277 + 1.278 +# define MOZ_BEGIN_ENUM_CLASS(...) MOZ_BEGIN_NESTED_ENUM_CLASS(__VA_ARGS__) 1.279 +# define MOZ_END_ENUM_CLASS(Name) \ 1.280 + MOZ_END_NESTED_ENUM_CLASS(Name) \ 1.281 + MOZ_FINISH_NESTED_ENUM_CLASS(Name) 1.282 + 1.283 +#endif /* __cplusplus */ 1.284 + 1.285 +#endif /* mozilla_TypedEnum_h */