tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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