mirror of
https://github.com/aantron/better-enums.git
synced 2025-12-06 16:56:42 +08:00
Made basic interface constexpr and made default cast checked.
This commit is contained in:
parent
2127b89dd4
commit
f55d2b56dc
6
Enum.h
6
Enum.h
@ -11,7 +11,6 @@
|
||||
|
||||
|
||||
|
||||
// TODO toUnderlying can probably be constexpr.
|
||||
// TODO Consider using a traits type instead of prefixing static members with an
|
||||
// underscore.
|
||||
// TODO Make appropriate changes in naming in EnumInternal.h. Most especially,
|
||||
@ -19,13 +18,8 @@
|
||||
// TODO Make the default cast checked.
|
||||
// TODO Create explicit members that do everything the constructors and
|
||||
// operators do, and define the operators in terms of those.
|
||||
// TODO Remove implicit conversion to underlying type.
|
||||
// TODO Rename Underlying so it doesn't clash with any value names.
|
||||
// TODO _first, _last, _count.
|
||||
// TODO Try to make names and values into values instead of functions.
|
||||
// TODO Move the member value into the base class and define most functions
|
||||
// there.
|
||||
// TODO Make sure to cast to the underlying type in to_int.
|
||||
|
||||
#define ENUM(EnumType, UnderlyingType, ...) \
|
||||
_ENUM_ARRAYS(EnumType, UnderlyingType, __VA_ARGS__); \
|
||||
|
||||
134
EnumInternal.h
134
EnumInternal.h
@ -39,6 +39,7 @@
|
||||
/// are very likely to collide.
|
||||
|
||||
// TODO Make it possible for enums to be map keys.
|
||||
// TODO Rename internal functions to match public interface conventions.
|
||||
|
||||
|
||||
|
||||
@ -295,6 +296,11 @@ constexpr bool _endsName(char c, size_t index = 0)
|
||||
_endsName(c, index + 1);
|
||||
}
|
||||
|
||||
constexpr char _toLowercaseAscii(char c)
|
||||
{
|
||||
return c >= 0x41 && c <= 0x5A ? c + 0x20 : c;
|
||||
}
|
||||
|
||||
/// Compile-time function that matches a stringized name (with potential
|
||||
/// trailing spaces and equals signs) against a reference name (a regular
|
||||
/// null-terminated string).
|
||||
@ -327,6 +333,20 @@ constexpr bool _namesMatch(const char *stringizedName,
|
||||
_namesMatch(stringizedName, referenceName, index + 1);
|
||||
}
|
||||
|
||||
constexpr bool _namesMatchNocase(const char *stringizedName,
|
||||
const char *referenceName,
|
||||
size_t index = 0)
|
||||
{
|
||||
return
|
||||
_endsName(stringizedName[index]) ? referenceName[index] == '\0' :
|
||||
referenceName[index] == '\0' ? false :
|
||||
_toLowercaseAscii(stringizedName[index]) !=
|
||||
_toLowercaseAscii(referenceName[index]) ? false :
|
||||
_namesMatchNocase(stringizedName, referenceName, index + 1);
|
||||
}
|
||||
|
||||
#define _ENUM_NOT_FOUND ((size_t)-1)
|
||||
|
||||
|
||||
|
||||
/// Functions and types used to compute range properties such as the minimum and
|
||||
@ -456,7 +476,7 @@ static inline const char * const* _processNames(const char * const *rawNames,
|
||||
|
||||
template <typename EnumType> class _GeneratedArrays;
|
||||
|
||||
// TODO Try to convert values array to _Enumerated[] instead of _Integral[].
|
||||
// TODO Move definitions to last macro.
|
||||
|
||||
#define _ENUM_ARRAYS(EnumType, UnderlyingType, ...) \
|
||||
class EnumType; \
|
||||
@ -518,13 +538,16 @@ class _Implementation : public _GeneratedArrays<EnumType> {
|
||||
return _value;
|
||||
}
|
||||
|
||||
// TODO This should be a checked cast.
|
||||
constexpr static EnumType _from_int(_Integral value)
|
||||
{
|
||||
return _value_array[_from_int_loop(value, true)];
|
||||
}
|
||||
|
||||
constexpr static EnumType _from_int_unchecked(_Integral value)
|
||||
{
|
||||
return (_Enumerated)value;
|
||||
}
|
||||
|
||||
// TODO Use iterables.
|
||||
const char* to_string() const
|
||||
{
|
||||
_processNames();
|
||||
@ -537,30 +560,29 @@ class _Implementation : public _GeneratedArrays<EnumType> {
|
||||
throw std::domain_error("Enum::_to_string: invalid enum value");
|
||||
}
|
||||
|
||||
// TODO Make constexpr.
|
||||
static EnumType _from_string(const char *name)
|
||||
constexpr static EnumType _from_string(const char *name)
|
||||
{
|
||||
_processNames();
|
||||
|
||||
for (size_t index = 0; index < _size; ++index) {
|
||||
if (strcmp(_processedNames[index], name) == 0)
|
||||
return _value_array[index];
|
||||
return _value_array[_from_string_loop(name, true)];
|
||||
}
|
||||
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
// TODO Make constexpr.
|
||||
static EnumType _from_string_nocase(const char *name)
|
||||
constexpr static EnumType _from_string_nocase(const char *name)
|
||||
{
|
||||
_processNames();
|
||||
|
||||
for (size_t index = 0; index < _size; ++index) {
|
||||
if (strcasecmp(_processedNames[index], name) == 0)
|
||||
return _value_array[index];
|
||||
return _value_array[_from_string_nocase_loop(name, true)];
|
||||
}
|
||||
|
||||
throw std::exception();
|
||||
constexpr static bool _is_valid(_Integral value)
|
||||
{
|
||||
return _from_int_loop(value, false) != _ENUM_NOT_FOUND;
|
||||
}
|
||||
|
||||
constexpr static bool _is_valid(const char *name)
|
||||
{
|
||||
return _from_string_loop(name, false) != _ENUM_NOT_FOUND;
|
||||
}
|
||||
|
||||
constexpr static bool _is_valid_nocase(const char *name)
|
||||
{
|
||||
return _from_string_nocase_loop(name, false) != _ENUM_NOT_FOUND;
|
||||
}
|
||||
|
||||
operator _Enumerated() { return _value; }
|
||||
@ -595,50 +617,46 @@ class _Implementation : public _GeneratedArrays<EnumType> {
|
||||
}
|
||||
|
||||
protected:
|
||||
// See comment by _isIterableIndex.
|
||||
#ifdef __clang__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wtautological-compare"
|
||||
#endif // #ifdef __clang__
|
||||
|
||||
// TODO Do a real check here - look it up in the array.
|
||||
template <typename IntegralType>
|
||||
static bool _is_valid(IntegralType value)
|
||||
constexpr static size_t _from_int_loop(_Integral value,
|
||||
bool throw_exception,
|
||||
size_t index = 0)
|
||||
{
|
||||
static_assert(std::is_integral<IntegralType>::value,
|
||||
"argument to EnumType::valid must have integral type");
|
||||
static_assert(std::is_signed<IntegralType>::value ==
|
||||
std::is_signed<_Integral>::value,
|
||||
"argument to EnumType::valid must be signed if and only "
|
||||
"if underlying type of EnumType is signed");
|
||||
|
||||
return true;
|
||||
return
|
||||
index == _size ?
|
||||
(throw_exception ?
|
||||
throw std::runtime_error(
|
||||
"Enum::from_int: invalid integer value") :
|
||||
_ENUM_NOT_FOUND) :
|
||||
_value_array[index] == value ? index :
|
||||
_from_int_loop(value, throw_exception, index + 1);
|
||||
}
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif // #ifdef __clang__
|
||||
|
||||
static bool _is_valid(const char *name)
|
||||
constexpr static size_t _from_string_loop(const char *name,
|
||||
bool throw_exception,
|
||||
size_t index = 0)
|
||||
{
|
||||
try {
|
||||
_from_string(name);
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
return false;
|
||||
}
|
||||
return
|
||||
index == _size ?
|
||||
(throw_exception ?
|
||||
throw std::runtime_error(
|
||||
"Enum::_from_string: invalid string argument") :
|
||||
_ENUM_NOT_FOUND) :
|
||||
_namesMatch(_name_array[index], name) ? index :
|
||||
_from_string_loop(name, throw_exception, index + 1);
|
||||
}
|
||||
|
||||
static bool _is_valid_nocase(const char *name)
|
||||
constexpr static size_t _from_string_nocase_loop(const char *name,
|
||||
bool throw_exception,
|
||||
size_t index = 0)
|
||||
{
|
||||
try {
|
||||
_from_string_nocase(name);
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
return false;
|
||||
}
|
||||
return
|
||||
index == _size ?
|
||||
(throw_exception ?
|
||||
throw std::runtime_error(
|
||||
"Enum::_from_string_nocase: invalid string argument") :
|
||||
_ENUM_NOT_FOUND) :
|
||||
_namesMatchNocase(_name_array[index], name) ? index :
|
||||
_from_string_nocase_loop(name, throw_exception, index + 1);
|
||||
}
|
||||
|
||||
// TODO Remove static casts wherever reasonable.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user