зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1371771 - Add a MOZ_DEFINE_ENUM macro and variants to MFBT. r=froydnj
The macro simultaneously declares an enumeration and a count of its enumerators. A few variants of the macro are also provided to handle things like enum classes, underlying types, and enumerations declared at class scope. MozReview-Commit-ID: 3z6yHnfXbLj --HG-- extra : rebase_source : 92c333693e4bbf85b89cd3d7ac5b31f4b5434367
This commit is contained in:
Родитель
1b11218ff1
Коммит
2c2d3ded79
|
@ -0,0 +1,146 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Poor man's reflection for enumerations. */
|
||||
|
||||
#ifndef mozilla_DefineEnum_h
|
||||
#define mozilla_DefineEnum_h
|
||||
|
||||
#include <stddef.h> // for size_t
|
||||
|
||||
#include "mozilla/MacroArgs.h" // for MOZ_ARG_COUNT
|
||||
#include "mozilla/MacroForEach.h" // for MOZ_FOR_EACH
|
||||
|
||||
/**
|
||||
* MOZ_UNWRAP_ARGS is a helper macro that unwraps a list of comma-separated
|
||||
* items enclosed in parentheses, to yield just the items.
|
||||
*
|
||||
* Usage: |MOZ_UNWRAP_ARGS foo| (note the absence of parentheses in the
|
||||
* invocation), where |foo| is a parenthesis-enclosed list.
|
||||
* For exampe if |foo| is |(3, 4, 5)|, then the expansion is just |3, 4, 5|.
|
||||
*/
|
||||
#define MOZ_UNWRAP_ARGS(...) __VA_ARGS__
|
||||
|
||||
/**
|
||||
* MOZ_DEFINE_ENUM(aEnumName, aEnumerators) is a macro that allows
|
||||
* simultaneously defining an enumeration named |aEnumName|, and a constant
|
||||
* that stores the number of enumerators it has.
|
||||
*
|
||||
* The motivation is to allow the enumeration to evolve over time without
|
||||
* either having to manually keep such a constant up to date, or having to
|
||||
* add a special "sentinel" enumerator for this purpose. (While adding a
|
||||
* "sentinel" enumerator is trivial, it causes headaches with "switch"
|
||||
* statements. We often try to write "switch" statements whose cases exhaust
|
||||
* the enumerators and don't have a "default" case, so that if a new
|
||||
* enumerator is added and we forget to handle it in the "switch", the
|
||||
* compiler points it out. But this means we need to explicitly handle the
|
||||
* sentinel in every "switch".)
|
||||
*
|
||||
* |aEnumerators| is expected to be a comma-separated list of enumerators,
|
||||
* enclosed in parentheses. The enumerators may NOT have associated
|
||||
* initializers (an attempt to have one will result in a compiler error).
|
||||
* This ensures that the enumerator values are in the range [0, N), where N
|
||||
* is the number of enumerators.
|
||||
*
|
||||
* The list of enumerators cannot contain a trailing comma. This is a
|
||||
* limitation of MOZ_FOR_EACH, which we use in the implementation; if
|
||||
* MOZ_FOR_EACH supported trailing commas, we could too.
|
||||
*
|
||||
* The generated constant has the name "k" + |aEnumName| + "Count", and type
|
||||
* "size_t". The enumeration and the constant are both defined in the scope
|
||||
* in which the macro is invoked.
|
||||
*
|
||||
* For convenience, a constant of the enumeration type named
|
||||
* "kHighest" + |aEnumName| is also defined, whose value is the highest
|
||||
* valid enumerator, assuming the enumerators have contiguous values starting
|
||||
* from 0.
|
||||
*
|
||||
* Invocation of the macro may be followed by a semicolon, if one prefers a
|
||||
* more declaration-like syntax.
|
||||
*
|
||||
* Example invocation:
|
||||
* MOZ_DEFINE_ENUM(MyEnum, (Foo, Bar, Baz));
|
||||
*
|
||||
* This expands to:
|
||||
* enum MyEnum { Foo, Bar, Baz };
|
||||
* constexpr size_t kMyEnumCount = 3;
|
||||
* constexpr MyEnum kHighestMyEnum = MyEnum(kMyEnumCount - 1);
|
||||
* // some static_asserts to ensure the values are in the range [0, 3)
|
||||
*
|
||||
* The macro also has several variants:
|
||||
*
|
||||
* - A |_CLASS| variant, which generates an |enum class| instead of
|
||||
* a plain enum.
|
||||
*
|
||||
* - A |_WITH_BASE| variant which generates an enum with a specified
|
||||
* underlying ("base") type, which is provided as an additional
|
||||
* argument in second position.
|
||||
*
|
||||
* - An |_AT_CLASS_SCOPE| variant, designed for enumerations defined
|
||||
* at class scope. For these, the generated constants are static,
|
||||
* and have names prefixed with "s" instead of "k" as per
|
||||
* naming convention.
|
||||
*
|
||||
* (and combinations of these).
|
||||
*/
|
||||
|
||||
/*
|
||||
* A helper macro for asserting that an enumerator does not have an initializer.
|
||||
*
|
||||
* The static_assert and the comparison to 0 are just scaffolding; the
|
||||
* important part is forming the expression |aEnumName::aEnumeratorDecl|.
|
||||
*
|
||||
* If |aEnumeratorDecl| is just the enumerator name without an identifier,
|
||||
* this expression compiles fine. However, if |aEnumeratorDecl| includes an
|
||||
* initializer, as in |eEnumerator = initializer|, then this will fail to
|
||||
* compile in expression context, since |eEnumerator| is not an lvalue.
|
||||
*
|
||||
* (The static_assert itself should always pass in the absence of the above
|
||||
* error, since you can't get a negative enumerator value without having
|
||||
* an initializer somewhere. It just provides a place to put the expression
|
||||
* we want to form.)
|
||||
*/
|
||||
#define MOZ_ASSERT_ENUMERATOR_HAS_NO_INITIALIZER(aEnumName, aEnumeratorDecl) \
|
||||
static_assert((aEnumName::aEnumeratorDecl) >= aEnumName(0), \
|
||||
"MOZ_DEFINE_ENUM does not allow enumerators to have initializers");
|
||||
|
||||
#define MOZ_DEFINE_ENUM_IMPL(aEnumName, aClassSpec, aBaseSpec, aEnumerators) \
|
||||
enum aClassSpec aEnumName aBaseSpec { MOZ_UNWRAP_ARGS aEnumerators }; \
|
||||
constexpr size_t k##aEnumName##Count = MOZ_ARG_COUNT aEnumerators; \
|
||||
constexpr aEnumName k##Highest##aEnumName = aEnumName(k##aEnumName##Count - 1); \
|
||||
MOZ_FOR_EACH(MOZ_ASSERT_ENUMERATOR_HAS_NO_INITIALIZER, (aEnumName,), aEnumerators)
|
||||
|
||||
#define MOZ_DEFINE_ENUM(aEnumName, aEnumerators) \
|
||||
MOZ_DEFINE_ENUM_IMPL(aEnumName, , , aEnumerators)
|
||||
|
||||
#define MOZ_DEFINE_ENUM_WITH_BASE(aEnumName, aBaseName, aEnumerators) \
|
||||
MOZ_DEFINE_ENUM_IMPL(aEnumName, , : aBaseName, aEnumerators)
|
||||
|
||||
#define MOZ_DEFINE_ENUM_CLASS(aEnumName, aEnumerators) \
|
||||
MOZ_DEFINE_ENUM_IMPL(aEnumName, class, , aEnumerators)
|
||||
|
||||
#define MOZ_DEFINE_ENUM_CLASS_WITH_BASE(aEnumName, aBaseName, aEnumerators) \
|
||||
MOZ_DEFINE_ENUM_IMPL(aEnumName, class, : aBaseName, aEnumerators)
|
||||
|
||||
#define MOZ_DEFINE_ENUM_AT_CLASS_SCOPE_IMPL(aEnumName, aClassSpec, aBaseSpec, aEnumerators) \
|
||||
enum aClassSpec aEnumName aBaseSpec { MOZ_UNWRAP_ARGS aEnumerators }; \
|
||||
constexpr static size_t s##aEnumName##Count = MOZ_ARG_COUNT aEnumerators; \
|
||||
constexpr static aEnumName s##Highest##aEnumName = aEnumName(s##aEnumName##Count - 1); \
|
||||
MOZ_FOR_EACH(MOZ_ASSERT_ENUMERATOR_HAS_NO_INITIALIZER, (aEnumName,), aEnumerators)
|
||||
|
||||
#define MOZ_DEFINE_ENUM_AT_CLASS_SCOPE(aEnumName, aEnumerators) \
|
||||
MOZ_DEFINE_ENUM_AT_CLASS_SCOPE_IMPL(aEnumName, , , aEnumerators)
|
||||
|
||||
#define MOZ_DEFINE_ENUM_WITH_BASE_AT_CLASS_SCOPE(aEnumName, aBaseName, aEnumerators) \
|
||||
MOZ_DEFINE_ENUM_AT_CLASS_SCOPE_IMPL(aEnumName, , : aBaseName, aEnumerators)
|
||||
|
||||
#define MOZ_DEFINE_ENUM_CLASS_AT_CLASS_SCOPE(aEnumName, aEnumerators) \
|
||||
MOZ_DEFINE_ENUM_AT_CLASS_SCOPE_IMPL(aEnumName, class, , aEnumerators)
|
||||
|
||||
#define MOZ_DEFINE_ENUM_CLASS_WITH_BASE_AT_CLASS_SCOPE(aEnumName, aBaseName, aEnumerators) \
|
||||
MOZ_DEFINE_ENUM_AT_CLASS_SCOPE_IMPL(aEnumName, class, : aBaseName, aEnumerators)
|
||||
|
||||
#endif // mozilla_DefineEnum_h
|
|
@ -33,6 +33,7 @@ EXPORTS.mozilla = [
|
|||
'Compression.h',
|
||||
'DebugOnly.h',
|
||||
'decimal/Decimal.h',
|
||||
'DefineEnum.h',
|
||||
'double-conversion/source/double-conversion.h',
|
||||
'double-conversion/source/utils.h',
|
||||
'DoublyLinkedList.h',
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/DefineEnum.h"
|
||||
|
||||
// Sanity test for MOZ_DEFINE_ENUM.
|
||||
|
||||
MOZ_DEFINE_ENUM(TestEnum1, (EnumeratorA, EnumeratorB, EnumeratorC));
|
||||
|
||||
static_assert(EnumeratorA == 0, "Unexpected enumerator value");
|
||||
static_assert(EnumeratorB == 1, "Unexpected enumerator value");
|
||||
static_assert(EnumeratorC == 2, "Unexpected enumerator value");
|
||||
static_assert(kHighestTestEnum1 == EnumeratorC, "Incorrect highest value");
|
||||
static_assert(kTestEnum1Count == 3, "Incorrect enumerator count");
|
||||
|
||||
// Sanity test for MOZ_DEFINE_ENUM_CLASS.
|
||||
|
||||
MOZ_DEFINE_ENUM_CLASS(TestEnum2, (A, B, C));
|
||||
|
||||
static_assert(TestEnum2::A == TestEnum2(0), "Unexpected enumerator value");
|
||||
static_assert(TestEnum2::B == TestEnum2(1), "Unexpected enumerator value");
|
||||
static_assert(TestEnum2::C == TestEnum2(2), "Unexpected enumerator value");
|
||||
static_assert(kHighestTestEnum2 == TestEnum2::C, "Incorrect highest value");
|
||||
static_assert(kTestEnum2Count == 3, "Incorrect enumerator count");
|
||||
|
||||
// TODO: Test that the _WITH_BASE variants generate enumerators with the
|
||||
// correct underlying types. To do this, we need an |UnderlyingType|
|
||||
// type trait, which needs compiler support (recent versions of
|
||||
// compilers in the GCC family provide an |__underlying_type| builtin
|
||||
// for this purpose.
|
||||
|
||||
// Sanity test for MOZ_DEFINE_ENUM[_CLASS]_AT_CLASS_SCOPE.
|
||||
|
||||
struct TestClass {
|
||||
MOZ_DEFINE_ENUM_AT_CLASS_SCOPE(
|
||||
TestEnum3, (
|
||||
EnumeratorA,
|
||||
EnumeratorB,
|
||||
EnumeratorC
|
||||
));
|
||||
|
||||
MOZ_DEFINE_ENUM_CLASS_AT_CLASS_SCOPE(
|
||||
TestEnum4, (
|
||||
A,
|
||||
B,
|
||||
C
|
||||
));
|
||||
|
||||
static_assert(EnumeratorA == 0, "Unexpected enumerator value");
|
||||
static_assert(EnumeratorB == 1, "Unexpected enumerator value");
|
||||
static_assert(EnumeratorC == 2, "Unexpected enumerator value");
|
||||
static_assert(sHighestTestEnum3 == EnumeratorC, "Incorrect highest value");
|
||||
static_assert(sTestEnum3Count == 3, "Incorrect enumerator count");
|
||||
|
||||
static_assert(TestEnum4::A == TestEnum4(0), "Unexpected enumerator value");
|
||||
static_assert(TestEnum4::B == TestEnum4(1), "Unexpected enumerator value");
|
||||
static_assert(TestEnum4::C == TestEnum4(2), "Unexpected enumerator value");
|
||||
static_assert(sHighestTestEnum4 == TestEnum4::C, "Incorrect highest value");
|
||||
static_assert(sTestEnum4Count == 3, "Incorrect enumerator count");
|
||||
};
|
||||
|
||||
|
||||
// Test that MOZ_DEFINE_ENUM doesn't allow giving enumerators initializers.
|
||||
|
||||
#ifdef CONFIRM_COMPILATION_ERRORS
|
||||
MOZ_DEFINE_ENUM_CLASS(EnumWithInitializer1, (A = -1, B, C))
|
||||
MOZ_DEFINE_ENUM_CLASS(EnumWithInitializer2, (A = 1, B, C))
|
||||
MOZ_DEFINE_ENUM_CLASS(EnumWithInitializer3, (A, B = 6, C))
|
||||
#endif
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
// Nothing to do here, all tests are static_asserts.
|
||||
return 0;
|
||||
}
|
|
@ -21,6 +21,7 @@ CppUnitTests([
|
|||
'TestCheckedInt',
|
||||
'TestCountPopulation',
|
||||
'TestCountZeroes',
|
||||
'TestDefineEnum',
|
||||
'TestDoublyLinkedList',
|
||||
'TestEndian',
|
||||
'TestEnumeratedArray',
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
[TestCheckedInt]
|
||||
[TestCountPopulation]
|
||||
[TestCountZeroes]
|
||||
[TestDefineEnum]
|
||||
[TestDllInterceptor]
|
||||
skip-if = os != 'win'
|
||||
[TestEndian]
|
||||
|
|
Загрузка…
Ссылка в новой задаче