/// @file EnumInternal.h /// Internal definitions for the enum type generator in `Enum.h`. /// /// Several definitions must precede the public `ENUM` macro and the interface /// defined in it. This includes helper classes and all `constexpr` functions, /// which cannot be forward-declared. In order to make `Enum.h` more readable, /// these definitions are placed into this file, which is included from /// `Enum.h`. /// /// Throughout the internal code, macro and template parameters named `EnumType` /// stand for the class types generated by the `ENUM` macro, while parameters /// named `EnumValue` stand for the internal C++ enum types. Roughly, /// `EnumValue == EnumType::_Value`. /// /// @todo Consider simplifying compile-time function signatures by combining /// arguments that don't change into a single `constexpr` object. /// @todo There is a way to perform all computation on the names and values /// arrays in a single pass, by requiring that all the special constants /// (such as `_bad`) appear at the end, and working back to front. It's not /// clear what kind of performance improvement this will give, as the /// current passes are already pretty fast, and the compile time is /// dominated by parsing and type checking of other code. /// @todo It's possible that reducing the number of redundant array accesses /// will improve compile time, but a stand-alone test suggests that the cost /// of these accesses is very small. /// @todo Generating the values array using the `_eat_assign` template is /// expensive, and the cost seems to be due to the instantiation of /// compile-time objects, not due to templates. Trying statement expressions /// (a GNU extension) didn't work, because statement expressions aren't /// allowed "at file scope" (in this case, within a class type declared at /// file scope). /// @todo `_enum::_special_names::_find` can terminate early after finding all /// four special names' indices. /// @todo Compile time is currently dominated by the cost of static /// instantiation. Try to reduce this cost by statically instantiating data /// structures for each type, then dynamically passing them to a small /// number of actual processing functions - which only have to be /// instantiated once for every different underlying type. Underlying types /// are very likely to collide. #pragma once #include // For size_t. #include // For string and memory routines. #include "EnumPreprocessorMap.h" /// Internal namespace for compile-time and private run-time functions used by /// the enum class generator. namespace _enum { /// Weak symbols to allow the same data structures to be defined statically in /// multiple translation units, then be collapsed to one definition by the /// linker. #define _ENUM_WEAK __attribute__((weak)) // Forward declaration of _Internal, for use in a friend declation in _Iterable. template class _Internal; /// Template for iterable objects over enum names and values. /// /// The iterables are intended for use with C++11 `for-each` syntax. They are /// returned by each enum type's static `names()` and `values()` methods. For /// example, `EnumType::values()` is an iterable over valid values of type /// `EnumType`, and allows the following form: /// /// ~~~{.cc} /// for (EnumType e : EnumType::values()) { /// // ... /// } /// ~~~ /// /// The iterable class is templated to reuse code between the name and value /// iterables. /// /// @tparam Element Type of element returned during iteration: either the enum /// type (for iterables over `values()`) or `const char*` (for iterables /// over `names()`). /// @tparam EnumType The enum type. /// @tparam ArrayType Type of the array actually being iterated over. The reason /// this is a type parameter is because for the iterable over `values()`, /// the underlying array type is `const EnumType::_value * const`, instead /// of `const EnumType * const`, as one might first expect. Objects of type /// `EnumType` are constructed on the fly during iteration from values of /// type `EnumType::_value` (this is a no-op at run-time). For iterables /// over `names()`, `ArrayType` is simply `const char * const`, as would be /// expeted. /// /// @todo Consider making `_Iterable` `constexpr`. /// @todo An iterator over valid values and an iterator over all values should /// really be different types. /// /// @internal /// /// An `_Iterable` stores a reference to the array (of either names or values) /// that will be iterated over. `_Iterable::iterator` additionally stores an /// index into the array. The iterator begins at the first valid index. Each /// time it is incremented, the iterator advances to the next valid index. The /// `end()` iterator stores an index equal to the size of the array. Values are /// considered valid if they are not equal to the bad value, are not below the /// minimum value, and are not above the maximum value. Names are valid if they /// are the name of a valid value. template class _Iterable { public: /// Iterators for iterating over enum names or values. class iterator { public: /// Returns the current name or value. Element operator *() const { return (Element)_arrayPointer[_index]; } /// Advances the iterator to the next valid name or value. If there is /// no such value, the iterator becomes equal to the result of /// `_Iterable::end()`. /// @return A reference to itself. iterator& operator ++() { if (_allValues) { if (_index < EnumType::_rawSize) { do { ++_index; } while(_index < EnumType::_rawSize && EnumType::_isSpecialIndex(_index)); } } else { if (_index <= EnumType::_highestValidIndex) { do { ++_index; } while(_index <= EnumType::_highestValidIndex && !EnumType::_isIterableIndex(_index)); } } return *this; } /// Compares two iterators for equality. /// @param other Another iterator over the same array. bool operator ==(const iterator &other) const { return (other._arrayPointer == _arrayPointer) && (other._index == _index); } /// Compares two iterators for equality - negated comparison. /// @param other Another iterator over the same array. bool operator !=(const iterator &other) const { return !(*this == other); } public: /// An iterator can be declared without initialization - in this case, /// its state is undefined. iterator() = default; private: /// Constructs an iterator over the given array, with the given starting /// index. This method is used only be the enclosing `_Iterable` class. /// @param arrayPointer Array that will be iterated over. /// @param allValues If `true`, the iterator "stops" at all values, /// whether they are valid or not. Otherwise, the iterator stops /// only at valid values (or their names). /// @param index Initial index into the array. This must be the index of /// a valid value. iterator(ArrayType arrayPointer, bool allValues, size_t index) : _arrayPointer(arrayPointer), _allValues(allValues), _index(index) { } /// Reference to the array being iterated. ArrayType _arrayPointer; /// Whether to return only valid values or all values. bool _allValues; /// Current index into the array. This is always either the index of a /// valid value or else it is equal to the size of the array. size_t _index; /// Permit `_Iterable` to create iterators. friend class _Iterable; }; /// Returns an iterator to the beginning of the name or value array. iterator begin() const { if (_allValues) { size_t firstIndex = 0; while (firstIndex < EnumType::_rawSize && EnumType::_isSpecialIndex(firstIndex)) { ++firstIndex; } return iterator(_arrayPointer, true, firstIndex); } else { size_t firstIndex = EnumType::_lowestValidIndex; while ((firstIndex <= EnumType::_highestValidIndex) && !EnumType::_isIterableIndex(firstIndex)) { ++firstIndex; } return iterator(_arrayPointer, false, firstIndex); } } /// Returns an iterator to the end of the name or value array. iterator end() const { if (_allValues) return iterator(_arrayPointer, true, EnumType::_rawSize); else { return iterator(_arrayPointer, false, EnumType::_highestValidIndex + 1); } } /// Returns the number of valid elements (names or values) in the iterable - /// the number of times an iterator starting at `begin()` can be /// dereferenced and then advanced before reaching `end()`. size_t size() const { return EnumType::size(); } private: /// Creates an `_Iterable` object over an array. _Iterable(ArrayType arrayPointer, bool allValues) : _arrayPointer(arrayPointer), _allValues(allValues) { } /// The array over which iteration will be performed. ArrayType _arrayPointer; /// Whether to return only valid values or all values. bool _allValues; /// Permit the enum class itself to create `_Iterable` objects. friend class _Internal; }; /// Compile-time helper class used to transform expressions of the forms `A` and /// `A = 42` into values of type `UnderlyingType` that can be used in /// initializer lists. The `ENUM` macro is passed a mixture of simple enum /// constants (`A`) and constants with an explicitly-assigned value (`A = 42`). /// Both must be turned into expressions of type `UnderlyingType` in order to be /// usable in initializer lists of the values array. This is done by prepending /// a cast to the expression, as follows: /// ~~~{.cc} /// (_eat_assign)A /// (_eat_assign)A = 42 /// ~~~ /// The second case is the interesting one. At compile time, the value `A` is /// first converted to an equivalent `_eat_assign` object, that /// stores the value. This object has an overriden assignment operator, which /// "eats" the `= 42` and returns the stored value of `A`, which is then used in /// the initializer list. /// @tparam UnderlyingType Final type used in the values array. template class _eat_assign { private: UnderlyingType _value; public: explicit constexpr _eat_assign(UnderlyingType value) : _value(value) { } constexpr UnderlyingType operator =(UnderlyingType dummy) { return _value; } constexpr operator UnderlyingType () { return _value; } }; /// Prepends its second argument with the cast `(_eat_assign)` /// in order to make it usable in initializer lists. See `_eat_assign`. #define _ENUM_EAT_ASSIGN_SINGLE(UnderlyingType, expression) \ ((_enum::_eat_assign)expression) /// Prepends each of its arguments with the casts /// `(_eat_assign)`, creating the elements of an initializer /// list of objects of type `UnderlyingType`. #define _ENUM_EAT_ASSIGN(UnderlyingType, ...) \ _ENUM_PP_MAP(_ENUM_EAT_ASSIGN_SINGLE, UnderlyingType, __VA_ARGS__) /// Stringizes its second argument. The first argument is not used - it is there /// only because `_ENUM_PP_MAP` expects it. #define _ENUM_STRINGIZE_SINGLE(ignored, expression) #expression /// Stringizes each of its arguments. #define _ENUM_STRINGIZE(...) \ _ENUM_PP_MAP(_ENUM_STRINGIZE_SINGLE, ignored, __VA_ARGS__) /// Symbols that end a constant name. Constant can be defined in several ways, /// for example: /// ~~~{.cc} /// A /// A = AnotherConstant /// A = 42 /// A=42 /// ~~~ /// These definitions are stringized in their entirety by `_ENUM_STRINGIZE`. /// This means that in addition to the actual constant names, the raw `_names` /// arrays potentially contain additional trailing symbols. `_ENUM_NAME_ENDERS` /// defines an array of symbols that would end the part of the string that is /// the actual constant name. Note that it is important that the null terminator /// is implicitly present in this array. #define _ENUM_NAME_ENDERS "= \t\n" /// Compile-time function that determines whether a character terminates the /// name portion of an enum constant definition. /// /// Call as `_endsName(c)`. /// /// @param c Character to be tested. /// @param index Current index into the `_ENUM_NAME_ENDERS` array. /// @return `true` if and only if `c` is one of the characters in /// `_ENUM_NAME_ENDERS`, including the implicit null terminator in that /// array. constexpr bool _endsName(char c, size_t index = 0) { return // First, test whether c is equal to the current character in // _ENUM_NAME_ENDERS. In the case where c is the null terminator, this // will cause _endsName to return true when it has exhausted // _ENUM_NAME_ENDERS. c == _ENUM_NAME_ENDERS[index] ? true : // If _ENUM_NAME_ENDERS has been exhausted and c never matched, return // false. _ENUM_NAME_ENDERS[index] == '\0' ? false : // Otherwise, go on to the next character in _ENUM_ENDERS. _endsName(c, index + 1); } /// Compile-time function that matches a stringized name (with potential /// trailing spaces and equals signs) against a reference name (a regular /// null-terminated string). /// /// Call as `_namesMatch(stringizedName, referenceName)`. /// /// @param stringizedName A stringized constant name, potentially terminated by /// one of the symbols in `_ENUM_NAME_ENDERS` instead of a null terminator. /// @param referenceName A name of interest. Null-terminated. /// @param index Current index into both names. /// @return `true` if and only if the portion of `stringizedName` before any of /// the symbols in `_ENUM_NAME_ENDERS` exactly matches `referenceName`. constexpr bool _namesMatch(const char *stringizedName, const char *referenceName, size_t index = 0) { return // If the current character in the stringized name is a name ender, // return true if the reference name ends as well, and false otherwise. _endsName(stringizedName[index]) ? referenceName[index] == '\0' : // The current character in the stringized name is not a name ender. If // the reference name ended, then it is too short, so return false. referenceName[index] == '\0' ? false : // Neither name has ended. If the two current characters don't match, // return false. stringizedName[index] != referenceName[index] ? false : // Otherwise, if the characters match, continue by comparing the rest of // the names. _namesMatch(stringizedName, referenceName, index + 1); } /// Represents invalid indices into the enum names and values arrays. #define _ENUM_NOT_FOUND ((size_t)-1) /// Functions and types used to search for the indices of special names (such as /// `_bad`.) The main purpose of this namespace is to shorten the identifiers /// used in the implementation of the search function. namespace _special_names { /// Name of the special constant for declaring the invalid value. #define _ENUM_BAD "_bad" /// Name of the special constant for declaring the default value. #define _ENUM_DEF "_def" /// Name of the special constant for declaring the minimum value. #define _ENUM_MIN "_min" /// Name of the special constant for declaring the maximum value. #define _ENUM_MAX "_max" #define _ENUM_SPECIAL_COUNT 4 /// Data returned by the `_enum::_special_names::_find` function. `_find` /// returns the index into the names array at which each one of `_bad`, `_def`, /// `_min`, and `_max` was found, or `_ENUM_NOT_FOUND` for each special name /// that wasn't found. It also returns the number of times each special name was /// found. During one run of `_find` on a correct enum declaration, an /// `_Indices` object is created at most five times. The last `_Indices` object /// is returned to the caller by copy. class _Indices { public: /// Last found indices for each of the special constants. size_t bad, def, min, max; /// Number of times each special constant was seen. int numberBad, numberDef, numberMin, numberMax; /// Creates the initial `_Indices` object, when no special names have been /// found. constexpr _Indices() : bad(_ENUM_NOT_FOUND), def(_ENUM_NOT_FOUND), min(_ENUM_NOT_FOUND), max(_ENUM_NOT_FOUND), numberBad(0), numberDef(0), numberMin(0), numberMax(0) { } private: /// Constructor used internally by the `foundXYZ` methods, each called when /// one of the special names is encountered. Sets the fields of the /// `_Indices` object. constexpr _Indices(size_t _bad, size_t _def, size_t _min, size_t _max, int _numberBad, int _numberDef, int _numberMin, int _numberMax) : bad(_bad), def(_def), min(_min), max(_max), numberBad(_numberBad), numberDef(_numberDef), numberMin(_numberMin), numberMax(_numberMax) { } public: /// Called by `_find` when `_bad` is found. Sets the index of the last match /// for `_bad` to the given index, and increments the number of times `_bad` /// has been found. Returns a new `_Indices` object reflecting this change. /// @param index Index at which `_bad` was found in the names array. /// @return The new `_Indices` object. constexpr const _Indices foundBad(size_t index) { return _Indices(index, def, min, max, numberBad + 1, numberDef, numberMin, numberMax); } /// Called by `_find` when `_def` is found. /// @see `foundBad` constexpr const _Indices foundDef(size_t index) { return _Indices(bad, index, min, max, numberBad, numberDef + 1, numberMin, numberMax); } /// Called by `_find` when `_min` is found. /// @see `foundBad` constexpr const _Indices foundMin(size_t index) { return _Indices(bad, def, index, max, numberBad, numberDef, numberMin + 1, numberMax); } /// Called by `_find` when `_max` is found. /// @see `foundBad` constexpr const _Indices foundMax(size_t index) { return _Indices(bad, def, min, index, numberBad, numberDef, numberMin, numberMax + 1); } }; /// Compile-time function that returns the indices of constants named `_bad`, /// `_def`, `_min`, and `_max`, if they are present, and the number of times /// each index was found. The search is done back-to-front, so that future /// versions of this function can take advantage of the likelihood that all the /// special names are found at the end of the enum declaration, and terminate /// early. /// /// Call as `_find(names, array_size - 1)`. /// /// @param names Enum constant names array. /// @param index Current index into the names array. /// @param indices Current search results. /// @return An `_Indices` object representing the search results. constexpr _Indices _find(const char * const *names, size_t index, const _Indices &indices = _Indices()) { return // If the index has been advanced (backward) to past the beginning of // the array, return the current search results by copy. index == (size_t)-1 ? indices : // The index is valid. As an optimization, check if the current name // begins with an underscore. If not, immediately go on to the next // index (in a backwards direction). names[index][0] != '_' ? _find(names, index - 1, indices) : // The index is valid and the name begins with an underscore. Compare // the entire name with each of the potential special names. If there is // a match, continue at next index with an updated _Indices object. // Otherwise, try the next special name. _namesMatch(names[index], _ENUM_BAD) ? _find(names, index - 1, indices.foundBad(index)) : _namesMatch(names[index], _ENUM_DEF) ? _find(names, index - 1, indices.foundDef(index)) : _namesMatch(names[index], _ENUM_MIN) ? _find(names, index - 1, indices.foundMin(index)) : _namesMatch(names[index], _ENUM_MAX) ? _find(names, index - 1, indices.foundMax(index)) : // If the name did not match any of the special names, continue at the // next index into the names array (in a backwards direction) with the // current indices object unchanged. _find(names, index - 1, indices); } } /// Compile-time function that determines whether a given index is one of the /// indices in `specialIndices`. After the enum generator finds the special /// names using `_enum::_special_names::_find`, it puts the four indices into an /// array of length 4. Indices for special names that weren't found are set to /// `_ENUM_NOT_FOUND`. Other functions, that run later, need to know whether /// they are dealing with one of these special indices or not. This function /// exists for that purpose. /// /// Call as `_isSpecial(specialIndices, specialIndexCount, someIndex)`. /// /// @param specialIndices Array of special indices. /// @param specialIndexCount Number of special indices. /// @param candidate A candidate index. /// @param index Current index into `specialIndices`. constexpr bool _isSpecial(const size_t *specialIndices, size_t specialIndexCount, size_t candidate, size_t index = 0) { return // If the index into specialIndices is equal to the number of such // special indices, then the candidate index was not found, so return // false. index == specialIndexCount ? false : // index is less than the count of special indices. If the candidate is // equal to the current special index, return true. candidate == specialIndices[index] ? true : // Otherwise, continue to the next special index. _isSpecial(specialIndices, specialIndexCount, candidate, index + 1); } /// Compile-time function that determines whether the value at one index is also /// present at another index in the enum values array by searching forward. /// Special constants such as `_bad` must be set to the value of another, /// non-special constant that is also declared in the enum type. This function /// and `_resolveReverse` check for this requirement by finding another index /// with the same value as the given index. `_resolveForward` is used for values /// that are likely to be found near the beginning of the enum declaration: /// `_min` and `_def`. /// /// Call as `_resolveForward(values, count, special, specialCount, value)`. /// /// @tparam UnderlyingType Type of elements in the values array. /// @param values Enum values array. /// @param valueCount Number of elements in `values`. /// @param specialIndices Special indices array. See `_isSpecial`. This array is /// used to reject values that are found at special indices - the value /// being resolved must be found at a non-special index. /// @param specialIndexCount Number of elements in `specialIndices`. /// @param specialValue Value to be resolved. /// @param index Current index into the `values` array. /// @return The non-special index at which `specialValue` is found, or /// `_ENUM_NOT_FOUND` if it is not found at all. template constexpr size_t _resolveForward(const UnderlyingType *values, size_t valueCount, const size_t *specialIndices, size_t specialIndexCount, UnderlyingType specialValue, size_t index = 0) { return // If iteration has reached the end of the values array, then the value // has not been found. index == valueCount ? _ENUM_NOT_FOUND : // index still points into the array. If the value at the index is equal // to the special value, and this is not a special index, then return // the index. values[index] == specialValue && !_isSpecial(specialIndices, specialIndexCount, index) ? index : // Otherwise, continue at the next index into the values array. _resolveForward(values, valueCount, specialIndices, specialIndexCount, specialValue, index + 1); } /// Compile-time function that resolves special values in a backwards direction. /// /// Call as `_resolveReverse(values, count, special, specialCount, value, /// count - 1)`. /// /// @see `_resolveForward` template constexpr size_t _resolveReverse(const UnderlyingType *values, const size_t *specialIndices, size_t specialIndexCount, UnderlyingType specialValue, size_t index) { return // The index is assumed to be valid upon entry into this function. // Immediately perform the same check as in _resolveForward. values[index] == specialValue && !_isSpecial(specialIndices, specialIndexCount, index) ? index : // If the value was not found at the current index, then, if the current // index is zero, the value is not present in values. index == 0 ? _ENUM_NOT_FOUND : // Otherwise, continue at the next (in a backwards direction) index into // the values array. _resolveReverse(values, specialIndices, specialIndexCount, specialValue, index - 1); } /// Compile-time function that returns the highest index lower than the initial /// value of `index` that is not a special index. Used to find the bad value if /// it is not explicitly given by supplying the `_bad` constant. /// /// Call as `_highestRegular(special, specialCount, valueCount - 1)`. /// /// @param specialIndices Array of special indices (indices of `_bad`, etc.) /// @param specialIndexCount Number of elements in `specialIndices`. /// @param index Current candidate index. This starts at the number of enum /// constants, minus one, and decreases until an index that is not in /// `specialIndices` is found. constexpr size_t _highestRegular(const size_t *specialIndices, size_t specialIndexCount, size_t index) { return // The current index is assumed to be valid. If it's not a special // index, return it. !_isSpecial(specialIndices, specialIndexCount, index) ? index : // If it's a special index and is zero, there are no lower non-special // indices - return _ENUM_NOT_FOUND. index == 0 ? _ENUM_NOT_FOUND : // Otherwise, continue the search at the next-lowest index. This cannot // happen more than four times because there are at most four special // indices. _highestRegular(specialIndices, specialIndexCount, index - 1); } /// Compile-time function that finds the lowest index that is not a special /// index, and is not the index of the bad value. The bad value is the one that /// is either set by `_bad`, or, if `_bad` is not given, it is the last regular /// (non-special) value declared in the enum type. /// /// Call as `_lowestValid(values, count, special, specialCount, badValue)`. /// /// @tparam UnderlyingType Type of elements in the values array. /// @param values Values array. /// @param valueCount Number of elements in the values array. /// @param specialIndices Special index array. /// @param specialIndexCount Number of elements in `specialIndices`. /// @param badValue The bad value. /// @param index Current index into `values`. template constexpr size_t _lowestValid(const UnderlyingType *values, size_t valueCount, const size_t *specialIndices, size_t specialIndexCount, UnderlyingType badValue, size_t index = 0) { return // If the values array has been exhausted without finding a valid index, // return _ENUM_NOT_FOUND. index == valueCount ? _ENUM_NOT_FOUND : // The values array has not been exhausted. If the current index is not // special, and the value at the index is not the bad value, return the // index. !_isSpecial(specialIndices, specialIndexCount, index) && values[index] != badValue ? index : // Otherwise, continue at the next index in the values array. _lowestValid(values, valueCount, specialIndices, specialIndexCount, badValue, index + 1); } /// Compile-time function that finds the highest index that is not a special /// index, and is not the index of the bad value. /// /// Call as `_highestValid(values, special, specialCount, badValue, /// valueCount - 1)`. /// /// @see `_lowestValid` template constexpr size_t _highestValid(const UnderlyingType *values, const size_t *specialIndices, size_t specialIndexCount, UnderlyingType badValue, size_t index) { return // The index is assumed to be in range upon entry into this function. If // it's not a special index, nor is the value at the index equal to the // bad value, return the index. !_isSpecial(specialIndices, specialIndexCount, index) && values[index] != badValue ? index : // Otherwise, if the index has reached zero, a valid index will not be // found. index == 0 ? _ENUM_NOT_FOUND : // The index is not valid and greater than zero - continue at the next // (decreasing) index into the values array. _highestValid(values, specialIndices, specialIndexCount, badValue, index - 1); } /// Functions and types used to compute range properties such as the minimum and /// maximum declared enum values, and the total number of valid enum values. namespace _range { /// Type of object returned by `_minMax`. Pair of the minimum and maximum value /// found. class _MinMax { public: size_t min, max; constexpr _MinMax(size_t _min, size_t _max) : min(_min), max(_max) { } }; /// Compile-time function that finds the default minimum and maximum values of /// an enum. Note that if the minimum and/or maximum value is overridden using /// `_min` and `_max`, the corresponding result of this function will be /// ignored. /// /// This function should be called with `bestMin` and `bestMax` set to the first /// valid (non-special, non-bad) index in the enumeration. One such index is /// guaranteed to exist by code that runs prior to where this function is /// called. /// /// @tparam UnderlyingType The enum underlying type. Comparisons are done at /// this type. Note that the signedness of this type affects the /// comparisons. /// @param values Enum values array. /// @param valueCount Number of values. /// @param specialIndices Special index array. /// @param specialIndexCount Number of special indices. /// @param badValue The bad value. /// @param index Current index in the iteration. This should initially be set to /// the index after the first valid index (add one to it). /// @param bestMin Index of the lowest valid value found so far. /// @param bestMax Index of the highest valid value found so far. template constexpr _MinMax _minMax(const UnderlyingType *values, size_t valueCount, const size_t *specialIndices, size_t specialIndexCount, UnderlyingType badValue, size_t index, size_t bestMin, size_t bestMax) { return // If the current index is at the end of the array, return the pair of // the best found minimum and maximum. index == valueCount ? _MinMax(bestMin, bestMax) : // If the current is index is special (is _bad, _def, _min, or _max), or // if the value at the current index is equal to the bad value, then // skip the current index - go on to the next one without updating the // min or max. _isSpecial(specialIndices, specialIndexCount, index) || values[index] == badValue ? _minMax(values, valueCount, specialIndices, specialIndexCount, badValue, index + 1, bestMin, bestMax) : // If the current value is higher than the best max so far, continue at // the next index with the best max index updated to the current index. // Note that it is not necessary to also check if the current value is // less than the best min - the min and max start at the same value, and // the min can never go above the max after that. This is an // optimization that saves a nontrivial amount of time. values[index] > values[bestMax] ? _minMax(values, valueCount, specialIndices, specialIndexCount, badValue, index + 1, bestMin, index) : // Otherwise, if the current value is not higher than the min, continue // at the next index. If the current value is less than the best min so // far, then do update the best min for the recursive call. _minMax(values, valueCount, specialIndices, specialIndexCount, badValue, index + 1, values[index] < values[bestMin] ? index : bestMin, bestMax); } /// Compile-time function that finds the "size" of the enum names and values /// arrays. The size is the number of constants that would be returned when /// iterating over the enum. Constants are returned when they are not special /// (`_bad`, `_def`, `_min`, or `_max`), not bad (not equal to `_bad` if `_bad` /// is defined, or not the last non-special constant otherwise), not less than /// the minimum constant, and not less than the maximum constant. /// /// Call as `_size(values, count, special, specialCount, bad, min, max)`. /// /// @tparam Underlying enum type. /// @param values Enum values. /// @param valueCount Size of the `values` array. /// @param specialIndices Indices of the special constants. /// @param specialIndexCount Number of special indices. /// @param badValue The bad value. /// @param min Minimum value. /// @param max Maximum value. /// @param index Current index in the scan over `values`. /// @param accumulator Number of valid constants found so far. template constexpr size_t _size(const UnderlyingType *values, size_t valueCount, const size_t *specialIndices, size_t specialIndexCount, UnderlyingType badValue, UnderlyingType min, UnderlyingType max, size_t index = 0, size_t accumulator = 0) { return // If the index has reached the end of values, return the number of // valid constants found. index == valueCount ? accumulator : // If the current index is special, or the value is bad, or the value is // below the min or above the max, continue scanning at the next index // without changing the accumulator. _isSpecial(specialIndices, specialIndexCount, index) || values[index] == badValue || values[index] < min || values[index] > max ? _size(values, valueCount, specialIndices, specialIndexCount, badValue, min, max, index + 1, accumulator) : // If the current index is none of the above, continue at the next index // and increment the accumulator to account for the current value. _size(values, valueCount, specialIndices, specialIndexCount, badValue, min, max, index + 1, accumulator + 1); } } // namespace _range } // namespace _enum // TODO Document reliance on the order of strings and constants being the same. // TODO Document naming convention: raw, blank, processed. // TODO Note that the static_assert for _rawSize > 0 never really gets a chance // to fail in practice, because the preprocessor macros break before that. // TODO Argue why there is always a first regular and a last regular. // TODO Document clang WAR for min and max. // TODO Default should be the first index that is not the invalid index. // TODO static asserts about the underlying type being an integral type. Allow // only the types supported by C++11 enum class. #define _ENUM_CONSTANT_RESOLVES_FORWARD(EnumType, ConstantIndex, ConstantName) \ static_assert(ConstantIndex == _ENUM_NOT_FOUND || \ _enum::_resolveForward(_values, _rawSize, _specialIndices, \ _ENUM_SPECIAL_COUNT, \ _values[ConstantIndex]) \ != _ENUM_NOT_FOUND, \ "special constant " ConstantName " must be equal to another "\ "constant"); #define _ENUM_CONSTANT_RESOLVES_REVERSE(EnumType, ConstantIndex, ConstantName) \ static_assert(ConstantIndex == _ENUM_NOT_FOUND || \ _enum::_resolveReverse(_values, _specialIndices, \ _ENUM_SPECIAL_COUNT, \ _values[ConstantIndex], _rawSize - 1) \ != _ENUM_NOT_FOUND, \ "special constant " ConstantName " must be equal to another "\ "constant"); namespace _enum { // TODO Consider reserving memory statically. This will probably entail a great // compile-time slowdown, however. static const char * const* _processNames(const char * const *rawNames, size_t count) { // Allocate the replacement names array. const char **processedNames = new const char*[count]; if (processedNames == nullptr) return nullptr; // Count the number of bytes needed in the replacement names array (an upper // bound). size_t bytesNeeded = 0; for (size_t index = 0; index < count; ++index) bytesNeeded += std::strlen(rawNames[index]) + 1; // Allocate memory for the string data. char *nameStorage = new char[bytesNeeded]; if (nameStorage == nullptr) { delete[] processedNames; return nullptr; } // Trim each name and place the result in storage, then save a pointer to // it. char *writePointer = nameStorage; for (size_t index = 0; index < count; ++index) { const char *nameEnd = std::strpbrk(rawNames[index], _ENUM_NAME_ENDERS); size_t symbolCount = nameEnd == nullptr ? std::strlen(rawNames[index]) : nameEnd - rawNames[index]; std::strncpy(writePointer, rawNames[index], symbolCount); processedNames[index] = writePointer; writePointer += symbolCount; *writePointer = '\0'; ++writePointer; } return processedNames; } template class _GeneratedArrays; #define _ENUM_ARRAYS(EnumType, UnderlyingType, ...) \ class EnumType; \ \ namespace _enum { \ \ template <> \ class _GeneratedArrays { \ public: \ enum _Value { __VA_ARGS__ }; \ \ using Underlying = UnderlyingType; \ \ protected: \ static constexpr Underlying _values[] = \ { _ENUM_EAT_ASSIGN(UnderlyingType, __VA_ARGS__) }; \ \ static constexpr const char *_names[] = \ { _ENUM_STRINGIZE(__VA_ARGS__) }; \ \ static constexpr size_t _rawSize = \ _ENUM_PP_COUNT(__VA_ARGS__); \ }; \ \ constexpr _GeneratedArrays::Underlying _ENUM_WEAK \ _GeneratedArrays::_values[]; \ \ constexpr const char * _ENUM_WEAK _GeneratedArrays::_names[]; \ \ \ template <> \ const char * const * _ENUM_WEAK _Internal::_processedNames = \ nullptr; \ \ } // TODO Compute first index for iteration while computing range properties. template class _Internal : public _GeneratedArrays { protected: using _arrays = _GeneratedArrays; using _arrays::_values; using _arrays::_names; using _arrays::_rawSize; public: using typename _arrays::_Value; using typename _arrays::Underlying; protected: static_assert(_rawSize > 0, "no constants defined in enum type"); static constexpr _enum::_special_names::_Indices _indices = _enum::_special_names::_find(_names, _rawSize - 1); static constexpr size_t _specialIndices[] = { _indices.bad, _indices.def, _indices.min, _indices.max }; _ENUM_CONSTANT_RESOLVES_REVERSE(EnumType, _indices.bad, _ENUM_BAD); _ENUM_CONSTANT_RESOLVES_FORWARD(EnumType, _indices.def, _ENUM_DEF); _ENUM_CONSTANT_RESOLVES_FORWARD(EnumType, _indices.min, _ENUM_MIN); _ENUM_CONSTANT_RESOLVES_REVERSE(EnumType, _indices.max, _ENUM_MAX); static constexpr size_t _badIndex = _indices.bad == _ENUM_NOT_FOUND ? _enum::_highestRegular(_specialIndices, _ENUM_SPECIAL_COUNT, _rawSize - 1) : _indices.bad; static_assert(_badIndex != _ENUM_NOT_FOUND, "_bad not defined and no regular constants in enum type"); static constexpr size_t _lowestValidIndex = _enum::_lowestValid(_values, _rawSize, _specialIndices, _ENUM_SPECIAL_COUNT, _values[_badIndex]); static constexpr size_t _highestValidIndex = _enum::_highestValid(_values, _specialIndices, _ENUM_SPECIAL_COUNT, _values[_badIndex], _rawSize - 1); static_assert(_lowestValidIndex != _ENUM_NOT_FOUND, "no valid (non-bad) constants in enum type"); static_assert(_highestValidIndex != _ENUM_NOT_FOUND, "no valid (non-bad) constants in enum type"); static constexpr size_t _defIndex = _indices.def == _ENUM_NOT_FOUND ? _lowestValidIndex : _indices.def; static constexpr _enum::_range::_MinMax _minMax = _enum::_range::_minMax(_values, _rawSize, _specialIndices, _ENUM_SPECIAL_COUNT, _values[_badIndex], _lowestValidIndex + 1, _lowestValidIndex, _lowestValidIndex); static constexpr size_t _minIndex = _indices.min == _ENUM_NOT_FOUND ? _minMax.min : _indices.min; static constexpr size_t _maxIndex = _indices.max == _ENUM_NOT_FOUND ? _minMax.max : _indices.max; static_assert(_values[_minIndex] <= _values[_maxIndex], "minimum constant has value greater than maximum constant"); static constexpr size_t _size = _enum::_range::_size(_values, _rawSize, _specialIndices, _ENUM_SPECIAL_COUNT, _values[_badIndex], _values[_minIndex], _values[_maxIndex]); static constexpr size_t _specialBadIndex = _indices.bad; static constexpr size_t _specialDefIndex = _indices.def; static constexpr size_t _specialMinIndex = _indices.min; static constexpr size_t _specialMaxIndex = _indices.max; static constexpr bool _isSpecialIndex(size_t index) { return index == _specialBadIndex ? true : index == _specialDefIndex ? true : index == _specialMinIndex ? true : index == _specialMaxIndex ? true : false; } // Clang complains about the comparison with "min" when the underlying type is // unsigned and "min" is 0. Disable that warning. GCC doesn't even have this // warning under this name (and does not complain). #ifdef __clang__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wtautological-compare" #endif // #ifdef __clang__ static constexpr bool _isIterableIndex(size_t index) { return _isSpecialIndex(index) ? false : _values[index] == _values[_badIndex] ? false : _values[index] < _values[_minIndex] ? false : _values[index] > _values[_maxIndex] ? false : true; } #ifdef __clang__ #pragma GCC diagnostic pop #endif // #ifdef __clang__ static const char * const *_processedNames; static void _processNames() { if (_processedNames == nullptr) _processedNames = _enum::_processNames(_names, _rawSize); } using ValueIterable = _Iterable; using NameIterable = _Iterable; friend ValueIterable; friend NameIterable; static ValueIterable values() { return ValueIterable(_values, false); } static ValueIterable allValues() { return ValueIterable(_values, true); } static NameIterable names() { _processNames(); return NameIterable(_processedNames, false); } static NameIterable allNames() { _processNames(); return NameIterable(_processedNames, true); } static const char* desc(EnumType value) { _processNames(); for (size_t index = 0; index < _rawSize; ++index) { if (_values[index] == value) return _processedNames[index]; } return _processedNames[_badIndex]; } static const char* descE(EnumType value) { const char *result = desc(value); // Note that this is a pointer comparison. Takes deliberate advantage of // the fact that exactly this pointer is returned by desc() in case of // failure. if (result == _processedNames[_badIndex]) { // TODO Throw an exception here. } else return result; } static EnumType find(const char *name) { _processNames(); for (size_t index = 0; index < _rawSize; ++index) { if (strcmp(_processedNames[index], name) == 0) return (EnumType)_values[index]; } return (EnumType)_values[_badIndex]; } static EnumType findE(const char *name) { EnumType result = find(name); if (result == (_Value)_values[_badIndex]) // TODO Throw an exception here. else return result; } static EnumType caseFind(const char *name) { _processNames(); for (size_t index = 0; index < _rawSize; ++index) { if (strcasecmp(_processedNames[index], name) == 0) return (EnumType)_values[index]; } return (EnumType)_values[_badIndex]; } static EnumType caseFindE(const char *name) { EnumType result = caseFind(name); if (result == (_Value)_values[_badIndex]) { // TODO Throw an exception here. } else return result; } // See comment by _isIterableIndex. #ifdef __clang__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wtautological-compare" #endif // #ifdef __clang__ template static bool valid(IntegralType value) { static_assert(is_integral::value, "argument to EnumType::valid must have integral type"); static_assert(is_signed::value == is_signed::value, "argument to EnumType::valid must be signed if and only " "if underlying type of EnumType is signed"); return value >= _values[_minIndex] && value <= _values[_maxIndex] && value != _values[_badIndex]; } #ifdef __clang__ #pragma GCC diagnostic pop #endif // #ifdef __clang__ static bool valid(const char *name) { EnumType value = find(name); return valid(value.toUnderlying()); } static bool caseValid(const char *name) { EnumType value = caseFind(name); return valid(value.toUnderlying()); } public: bool operator ==(const EnumType &other) const { return static_cast(*this)._value == other._value; } bool operator ==(const _Value value) const { return static_cast(*this)._value == value; } template bool operator ==(T other) const = delete; bool operator !=(const EnumType &other) const { return !(*this == other); } bool operator !=(const _Value value) const { return !(*this == value); } template bool operator !=(T other) const = delete; bool operator <(const EnumType &other) const { return static_cast(*this)._value < other._value; } bool operator <(const _Value value) const { return static_cast(*this)._value < value; } template bool operator <(T other) const = delete; bool operator <=(const EnumType &other) const { return static_cast(*this)._value <= other._value; } bool operator <=(const _Value value) const { return static_cast(*this)._value <= value; } template bool operator <=(T other) const = delete; bool operator >(const EnumType &other) const { return static_cast(*this)._value > other._value; } bool operator >(const _Value value) const { return static_cast(*this)._value > value; } template bool operator >(T other) const = delete; bool operator >=(const EnumType &other) const { return static_cast(*this)._value >= other._value; } bool operator >=(const _Value value) const { return static_cast(*this)._value >= value; } template bool operator >=(T other) const = delete; int operator -() const = delete; template int operator +(T other) const = delete; template int operator -(T other) const = delete; template int operator *(T other) const = delete; template int operator /(T other) const = delete; template int operator %(T other) const = delete; template int operator <<(T other) const = delete; template int operator >>(T other) const = delete; int operator ~() const = delete; template int operator &(T other) const = delete; template int operator |(T other) const = delete; template int operator ^(T other) const = delete; int operator !() const = delete; template int operator &&(T other) const = delete; template int operator ||(T other) const = delete; }; }