summaryrefslogtreecommitdiffstats
path: root/mfbt/DefineEnum.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--mfbt/DefineEnum.h156
1 files changed, 156 insertions, 0 deletions
diff --git a/mfbt/DefineEnum.h b/mfbt/DefineEnum.h
new file mode 100644
index 0000000000..afcff10e52
--- /dev/null
+++ b/mfbt/DefineEnum.h
@@ -0,0 +1,156 @@
+/* -*- 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 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 turning on a bit can only increase an integer value. It just
+ * provides a place to put the expression we want to form.)
+ */
+
+#define MOZ_ASSERT_ENUMERATOR_HAS_NO_INITIALIZER(aEnumName, aEnumeratorDecl) \
+ static_assert( \
+ int(aEnumName::aEnumeratorDecl) <= \
+ (int(aEnumName::aEnumeratorDecl) | 1), \
+ "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 kHighest##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 sHighest##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