mirror of
https://github.com/aantron/better-enums.git
synced 2025-12-10 02:36:41 +08:00
1256 lines
54 KiB
C++
1256 lines
54 KiB
C++
/// @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 <cstddef> // For size_t.
|
|
#include <cstring> // 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 <typename EnumType> 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 <typename Element, typename EnumType, typename ArrayType>
|
|
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<EnumType>;
|
|
};
|
|
|
|
|
|
|
|
/// 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<UnderlyingType>)A
|
|
/// (_eat_assign<UnderlyingType>)A = 42
|
|
/// ~~~
|
|
/// The second case is the interesting one. At compile time, the value `A` is
|
|
/// first converted to an equivalent `_eat_assign<UnderlyingType>` 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 <typename UnderlyingType>
|
|
class _eat_assign {
|
|
private:
|
|
UnderlyingType _value;
|
|
|
|
public:
|
|
explicit constexpr _eat_assign(UnderlyingType value) : _value(value) { }
|
|
constexpr UnderlyingType operator =(UnderlyingType dummy) const
|
|
{ return _value; }
|
|
constexpr operator UnderlyingType () const { return _value; }
|
|
};
|
|
|
|
/// Prepends its second argument with the cast `(_eat_assign<UnderlyingType>)`
|
|
/// in order to make it usable in initializer lists. See `_eat_assign`.
|
|
#define _ENUM_EAT_ASSIGN_SINGLE(UnderlyingType, expression) \
|
|
((_enum::_eat_assign<UnderlyingType>)expression)
|
|
|
|
/// Prepends each of its arguments with the casts
|
|
/// `(_eat_assign<UnderlyingType>)`, 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) const
|
|
{ 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) const
|
|
{ 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) const
|
|
{ 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) const
|
|
{ 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 <typename UnderlyingType>
|
|
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 <typename UnderlyingType>
|
|
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 <typename UnderlyingType>
|
|
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 <typename UnderlyingType>
|
|
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 <typename UnderlyingType>
|
|
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 <typename UnderlyingType>
|
|
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 <typename EnumType> class _GeneratedArrays;
|
|
|
|
#define _ENUM_ARRAYS(EnumType, UnderlyingType, ...) \
|
|
class EnumType; \
|
|
\
|
|
namespace _enum { \
|
|
\
|
|
template <> \
|
|
class _GeneratedArrays<EnumType> { \
|
|
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<EnumType>::Underlying _ENUM_WEAK \
|
|
_GeneratedArrays<EnumType>::_values[]; \
|
|
\
|
|
constexpr const char * _ENUM_WEAK _GeneratedArrays<EnumType>::_names[]; \
|
|
\
|
|
\
|
|
template <> \
|
|
const char * const * _ENUM_WEAK _Internal<EnumType>::_processedNames = \
|
|
nullptr; \
|
|
\
|
|
}
|
|
|
|
// TODO Compute first index for iteration while computing range properties.
|
|
|
|
template <typename EnumType>
|
|
class _Internal : public _GeneratedArrays<EnumType> {
|
|
protected:
|
|
using _arrays = _GeneratedArrays<EnumType>;
|
|
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<const EnumType, EnumType, const Underlying * const>;
|
|
using NameIterable =
|
|
_Iterable<const char*, EnumType, const char * const*>;
|
|
|
|
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 <typename IntegralType>
|
|
static bool valid(IntegralType value)
|
|
{
|
|
static_assert(is_integral<IntegralType>::value,
|
|
"argument to EnumType::valid must have integral type");
|
|
static_assert(is_signed<IntegralType>::value ==
|
|
is_signed<Underlying>::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<const EnumType&>(*this)._value == other._value; }
|
|
bool operator ==(const _Value value) const
|
|
{ return static_cast<const EnumType&>(*this)._value == value; }
|
|
template <typename T> 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 <typename T> bool operator !=(T other) const = delete;
|
|
|
|
bool operator <(const EnumType &other) const
|
|
{ return static_cast<const EnumType&>(*this)._value < other._value; }
|
|
bool operator <(const _Value value) const
|
|
{ return static_cast<const EnumType&>(*this)._value < value; }
|
|
template <typename T> bool operator <(T other) const = delete;
|
|
|
|
bool operator <=(const EnumType &other) const
|
|
{ return static_cast<const EnumType&>(*this)._value <= other._value; }
|
|
bool operator <=(const _Value value) const
|
|
{ return static_cast<const EnumType&>(*this)._value <= value; }
|
|
template <typename T> bool operator <=(T other) const = delete;
|
|
|
|
bool operator >(const EnumType &other) const
|
|
{ return static_cast<const EnumType&>(*this)._value > other._value; }
|
|
bool operator >(const _Value value) const
|
|
{ return static_cast<const EnumType&>(*this)._value > value; }
|
|
template <typename T> bool operator >(T other) const = delete;
|
|
|
|
bool operator >=(const EnumType &other) const
|
|
{ return static_cast<const EnumType&>(*this)._value >= other._value; }
|
|
bool operator >=(const _Value value) const
|
|
{ return static_cast<const EnumType&>(*this)._value >= value; }
|
|
template <typename T> bool operator >=(T other) const = delete;
|
|
|
|
int operator -() const = delete;
|
|
template <typename T> int operator +(T other) const = delete;
|
|
template <typename T> int operator -(T other) const = delete;
|
|
template <typename T> int operator *(T other) const = delete;
|
|
template <typename T> int operator /(T other) const = delete;
|
|
template <typename T> int operator %(T other) const = delete;
|
|
|
|
template <typename T> int operator <<(T other) const = delete;
|
|
template <typename T> int operator >>(T other) const = delete;
|
|
|
|
int operator ~() const = delete;
|
|
template <typename T> int operator &(T other) const = delete;
|
|
template <typename T> int operator |(T other) const = delete;
|
|
template <typename T> int operator ^(T other) const = delete;
|
|
|
|
int operator !() const = delete;
|
|
template <typename T> int operator &&(T other) const = delete;
|
|
template <typename T> int operator ||(T other) const = delete;
|
|
};
|
|
|
|
}
|