mirror of
https://github.com/aantron/better-enums.git
synced 2026-01-01 03:12:09 +08:00
Added non-throwing versions of enum introduction functions.
These return values of an optional type better_enums::optional<T>. This type is defined in the spirit of boost::optional<T> and std::optional<T>, but is easy to manipulate at compile time. Two additional macros BETTER_ENUMS_USE_OPTIONAL and BETTER_ENUMS_EXTRA_INCLUDE are honored, whose intent is for the user to be able to inject an alternative option type. However, there are currently no viable alternatives. boost::optional<T> does not play well with constexpr, and I failed to make the code compile with std::optional<T>. I did not try very hard. I intend to support std::optional<T> in the future. Perhaps it will be the default, when available.
This commit is contained in:
parent
d9bd109172
commit
08dbe47edd
175
enum.h
175
enum.h
@ -10,14 +10,16 @@
|
||||
|
||||
#include <cstddef> // For size_t.
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
#ifdef BETTER_ENUMS_EXTRA_INCLUDE
|
||||
# include BETTER_ENUMS_EXTRA_INCLUDE
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef BETTER_ENUMS_MACRO_FILE
|
||||
|
||||
#include BETTER_ENUMS_MACRO_FILE
|
||||
|
||||
# include BETTER_ENUMS_MACRO_FILE
|
||||
#else
|
||||
|
||||
#define _ENUM_PP_MAP(macro, data, ...) \
|
||||
@ -335,10 +337,78 @@
|
||||
|
||||
|
||||
|
||||
namespace better_enums {
|
||||
|
||||
#ifdef BETTER_ENUMS_USE_OPTIONAL
|
||||
|
||||
template <typename T>
|
||||
using optional = BETTER_ENUMS_USE_OPTIONAL<T>;
|
||||
|
||||
#else
|
||||
|
||||
template <typename T>
|
||||
constexpr T _default()
|
||||
{
|
||||
return (typename T::_Enumerated)0;
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr const char* _default<const char*>()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr size_t _default<size_t>()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct optional {
|
||||
constexpr optional() : _valid(false), _value(_default<T>()) { }
|
||||
constexpr optional(T value) : _valid(true), _value(value) { }
|
||||
|
||||
constexpr const T& operator *() const { return _value; }
|
||||
constexpr const T& operator ->() const { return _value; }
|
||||
|
||||
constexpr operator bool() const { return _valid; }
|
||||
|
||||
constexpr const T& value() const { return _value; }
|
||||
|
||||
private:
|
||||
bool _valid;
|
||||
T _value;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
namespace _enum {
|
||||
|
||||
|
||||
|
||||
template <typename T>
|
||||
using _optional = better_enums::optional<T>;
|
||||
|
||||
template <typename CastTo, typename Element>
|
||||
constexpr static _optional<CastTo>
|
||||
_map_index(const Element *array, _optional<size_t> index)
|
||||
{
|
||||
return index ? (CastTo)array[*index] : _optional<CastTo>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr static T _or_throw(_optional<T> maybe, const char *message)
|
||||
{
|
||||
return maybe ? *maybe : throw std::runtime_error(message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename Element>
|
||||
struct _Iterable {
|
||||
using iterator = const Element*;
|
||||
@ -547,7 +617,6 @@ constexpr const _Iterable<const char*> _names{_name_array, _size}; \
|
||||
}
|
||||
|
||||
#define _ENUM_NS(EnumType) _enum::_data_ ## EnumType
|
||||
#define _ENUM_NOT_FOUND ((size_t)-1)
|
||||
|
||||
#ifndef BETTER_ENUMS_SAFER_SWITCH
|
||||
|
||||
@ -568,6 +637,9 @@ class EnumType : public _ENUM_NS(EnumType)::_Base { \
|
||||
constexpr static auto _value_array = _ENUM_NS(EnumType)::_value_array; \
|
||||
constexpr static auto _name_array = _ENUM_NS(EnumType)::_name_array; \
|
||||
\
|
||||
template <typename T> \
|
||||
using _optional = better_enums::optional<T>; \
|
||||
\
|
||||
public: \
|
||||
using _Integral = Integral; \
|
||||
\
|
||||
@ -583,9 +655,18 @@ class EnumType : public _ENUM_NS(EnumType)::_Base { \
|
||||
return _value; \
|
||||
} \
|
||||
\
|
||||
constexpr static const _optional<EnumType> \
|
||||
_from_integral_nothrow(_Integral value) \
|
||||
{ \
|
||||
return \
|
||||
_enum::_map_index<EnumType>(_value_array, _from_int_loop(value)); \
|
||||
} \
|
||||
\
|
||||
constexpr static const EnumType _from_integral(_Integral value) \
|
||||
{ \
|
||||
return _value_array[_from_int_loop(value, true)]; \
|
||||
return \
|
||||
_enum::_or_throw(_from_integral_nothrow(value), \
|
||||
"_from_integral_or_throw: invalid argument"); \
|
||||
} \
|
||||
\
|
||||
constexpr static const EnumType _from_integral_unchecked(_Integral value) \
|
||||
@ -595,32 +676,55 @@ class EnumType : public _ENUM_NS(EnumType)::_Base { \
|
||||
\
|
||||
constexpr const char* to_string() const \
|
||||
{ \
|
||||
return _name_array[_from_int_loop(_value, true)]; \
|
||||
return \
|
||||
_enum::_or_throw( \
|
||||
_enum::_map_index<const char*>(_name_array, \
|
||||
_from_int_loop(_value)), \
|
||||
"to_string: invalid enum value"); \
|
||||
} \
|
||||
\
|
||||
constexpr static const _optional<EnumType> \
|
||||
_from_string_nothrow(const char *name) \
|
||||
{ \
|
||||
return \
|
||||
_enum::_map_index<EnumType>(_value_array, _from_string_loop(name));\
|
||||
} \
|
||||
\
|
||||
constexpr static const EnumType _from_string(const char *name) \
|
||||
{ \
|
||||
return _value_array[_from_string_loop(name, true)]; \
|
||||
return \
|
||||
_enum::_or_throw(_from_string_nothrow(name), \
|
||||
"_from_string_or_throw: invalid argument"); \
|
||||
} \
|
||||
\
|
||||
constexpr static const _optional<EnumType> \
|
||||
_from_string_nocase_nothrow(const char *name) \
|
||||
{ \
|
||||
return \
|
||||
_enum::_map_index<EnumType>(_value_array, \
|
||||
_from_string_nocase_loop(name)); \
|
||||
} \
|
||||
\
|
||||
constexpr static const EnumType _from_string_nocase(const char *name) \
|
||||
{ \
|
||||
return _value_array[_from_string_nocase_loop(name, true)]; \
|
||||
return \
|
||||
_enum::_or_throw(_from_string_nocase_nothrow(name), \
|
||||
"_from_string_nocase_or_throw: invalid argument");\
|
||||
} \
|
||||
\
|
||||
constexpr static bool _is_valid(_Integral value) \
|
||||
{ \
|
||||
return _from_int_loop(value, false) != _ENUM_NOT_FOUND; \
|
||||
return _from_int_loop(value); \
|
||||
} \
|
||||
\
|
||||
constexpr static bool _is_valid(const char *name) \
|
||||
{ \
|
||||
return _from_string_loop(name, false) != _ENUM_NOT_FOUND; \
|
||||
return _from_string_loop(name); \
|
||||
} \
|
||||
\
|
||||
constexpr static bool _is_valid_nocase(const char *name) \
|
||||
{ \
|
||||
return _from_string_nocase_loop(name, false) != _ENUM_NOT_FOUND; \
|
||||
return _from_string_nocase_loop(name); \
|
||||
} \
|
||||
\
|
||||
_ENUM_CONVERSION_FOR_SWITCH(Integral, __VA_ARGS__); \
|
||||
@ -629,46 +733,33 @@ class EnumType : public _ENUM_NS(EnumType)::_Base { \
|
||||
constexpr static auto &_names = _ENUM_NS(EnumType)::_names; \
|
||||
\
|
||||
protected: \
|
||||
constexpr static size_t _from_int_loop(_Integral value, \
|
||||
bool throw_exception, \
|
||||
size_t index = 0) \
|
||||
constexpr static _optional<size_t> _from_int_loop(_Integral value, \
|
||||
size_t index = 0) \
|
||||
{ \
|
||||
return \
|
||||
index == _size ? \
|
||||
(throw_exception ? \
|
||||
throw std::runtime_error( \
|
||||
"Enum::_from_integral: invalid integer value") : \
|
||||
_ENUM_NOT_FOUND) : \
|
||||
_value_array[index]._value == value ? index : \
|
||||
_from_int_loop(value, throw_exception, index + 1); \
|
||||
index == _size ? _optional<size_t>() : \
|
||||
_value_array[index]._value == value ? _optional<size_t>(index) : \
|
||||
_from_int_loop(value, index + 1); \
|
||||
} \
|
||||
\
|
||||
constexpr static size_t _from_string_loop(const char *name, \
|
||||
bool throw_exception, \
|
||||
size_t index = 0) \
|
||||
constexpr static _optional<size_t> _from_string_loop(const char *name, \
|
||||
size_t index = 0) \
|
||||
{ \
|
||||
return \
|
||||
index == _size ? \
|
||||
(throw_exception ? \
|
||||
throw std::runtime_error( \
|
||||
"Enum::_from_string: invalid string argument") : \
|
||||
_ENUM_NOT_FOUND) : \
|
||||
_enum::_namesMatch(_name_array[index], name) ? index : \
|
||||
_from_string_loop(name, throw_exception, index + 1); \
|
||||
index == _size ? _optional<size_t>() : \
|
||||
_enum::_namesMatch(_name_array[index], name) ? \
|
||||
_optional<size_t>(index) : \
|
||||
_from_string_loop(name, index + 1); \
|
||||
} \
|
||||
\
|
||||
constexpr static size_t _from_string_nocase_loop(const char *name, \
|
||||
bool throw_exception, \
|
||||
size_t index = 0) \
|
||||
constexpr static _optional<size_t> \
|
||||
_from_string_nocase_loop(const char *name, size_t index = 0) \
|
||||
{ \
|
||||
return \
|
||||
index == _size ? \
|
||||
(throw_exception ? \
|
||||
throw std::runtime_error( \
|
||||
"Enum::_from_string_nocase: invalid string argument") :\
|
||||
_ENUM_NOT_FOUND) : \
|
||||
_enum::_namesMatchNocase(_name_array[index], name) ? index : \
|
||||
_from_string_nocase_loop(name, throw_exception, index + 1); \
|
||||
index == _size ? _optional<size_t>() : \
|
||||
_enum::_namesMatchNocase(_name_array[index], name) ? \
|
||||
_optional<size_t>(index) : \
|
||||
_from_string_nocase_loop(name, index + 1); \
|
||||
} \
|
||||
};
|
||||
|
||||
|
||||
@ -63,6 +63,18 @@ int main()
|
||||
|
||||
|
||||
|
||||
// Conversions with the nothrow (optional) interface.
|
||||
auto maybe_channel = Channel::_from_string_nothrow("foo");
|
||||
if (maybe_channel)
|
||||
throw std::logic_error("expected conversion failure");
|
||||
|
||||
maybe_channel = Channel::_from_string_nothrow("Blue");
|
||||
if (!maybe_channel)
|
||||
throw std::logic_error("expected successful conversion");
|
||||
print_channel(*maybe_channel);
|
||||
|
||||
|
||||
|
||||
// Unsafe unchecked cast.
|
||||
channel = Channel::_from_integral_unchecked(2);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user