diff --git a/enum.h b/enum.h index 1c8a312..c33d679 100644 --- a/enum.h +++ b/enum.h @@ -94,7 +94,7 @@ namespace _enum { template class _Iterable; -template +template class _BaseIterator { public: Derived& operator ++() @@ -112,12 +112,12 @@ class _BaseIterator { template class _ValueIterator : - public _BaseIterator> { + public _BaseIterator<_ValueIterator> { - using _Super = _BaseIterator>; + using _Super = _BaseIterator<_ValueIterator>; public: - constexpr EnumType operator *() const + constexpr typename EnumType::Enumerated operator *() const { return EnumType::_value_array[_Super::_index]; } private: @@ -128,9 +128,9 @@ class _ValueIterator : template class _NameIterator : - public _BaseIterator> { + public _BaseIterator<_NameIterator> { - using _Super = _BaseIterator>; + using _Super = _BaseIterator<_NameIterator>; public: const char* operator *() const @@ -148,8 +148,8 @@ class _Iterable { using iterator = Iterator; constexpr iterator begin() const { return iterator(0); } - constexpr iterator end() const { return iterator(EnumType::_size); } - constexpr size_t size() const { return EnumType::size(); } + constexpr iterator end() const { return iterator(EnumType::size); } + constexpr size_t size() const { return EnumType::size; } private: constexpr _Iterable() { }; @@ -192,7 +192,7 @@ class _eat_assign { /// 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) + ((_enum::_eat_assign)UnderlyingType::expression) /// Prepends each of its arguments with the casts /// `(_eat_assign)`, creating the elements of an initializer @@ -387,30 +387,27 @@ static inline const char * const* _processNames(const char * const *rawNames, return processedNames; } -#define _ENUM_TAG(EnumType) _tag_ ## EnumType -#define _ENUM_TAG_DECLARATION(EnumType) \ - namespace _enum { \ - struct _ENUM_TAG(EnumType); \ - } +template class _GeneratedArrays; -template class _GeneratedArrays; +} // namespace _enum -#define _ENUM_ARRAYS(EnumType, Integral, Tag, ...) \ +#define _ENUM_ARRAYS(EnumType, IntegralType, ...) \ + enum class EnumType : IntegralType { __VA_ARGS__ }; \ + \ namespace _enum { \ \ template <> \ - class _GeneratedArrays { \ + class _GeneratedArrays { \ protected: \ - using _Integral = Integral; \ + using Integral = IntegralType; \ + using Enumerated = EnumType; \ \ public: \ - constexpr static const char* _name = #EnumType; \ - \ - enum _Enumerated : _Integral { __VA_ARGS__ }; \ + constexpr static const char* name = #EnumType; \ \ protected: \ - constexpr static _Enumerated _value_array[] = \ - { _ENUM_EAT_ASSIGN(_Enumerated, __VA_ARGS__) }; \ + constexpr static Enumerated _value_array[] = \ + { _ENUM_EAT_ASSIGN(Enumerated, __VA_ARGS__) }; \ \ constexpr static const char *_name_array[] = \ { _ENUM_STRINGIZE(__VA_ARGS__) }; \ @@ -420,94 +417,92 @@ template class _GeneratedArrays; #define _ENUM_NOT_FOUND ((size_t)-1) -template -class _Enum : public _GeneratedArrays { +namespace enum_ { + +template +class traits : public _enum::_GeneratedArrays { protected: - using _arrays = _GeneratedArrays; + using _arrays = _enum::_GeneratedArrays; using _arrays::_value_array; using _arrays::_name_array; public: - using typename _arrays::_Enumerated; - using typename _arrays::_Integral; + using typename _arrays::Enumerated; + using typename _arrays::Integral; - constexpr static const size_t _size = - sizeof(_value_array) / sizeof(_Enumerated); - static_assert(_size > 0, "no constants defined in enum type"); + constexpr static const size_t size = + sizeof(_value_array) / sizeof(Enumerated); + static_assert(size > 0, "no constants defined in enum type"); - constexpr static const _Enumerated _first = _value_array[0]; - constexpr static const _Enumerated _last = _value_array[_size - 1]; - constexpr static const _Enumerated _min = _findMin(_value_array, _size); - constexpr static const _Enumerated _max = _findMax(_value_array, _size); + constexpr static const Enumerated first = _value_array[0]; + constexpr static const Enumerated last = _value_array[size - 1]; + constexpr static const Enumerated min = + _enum::_findMin(_value_array, size); + constexpr static const Enumerated max = + _enum::_findMax(_value_array, size); - constexpr static const size_t _span = _max - _min + 1; + constexpr static const size_t span = + (Integral)max - (Integral)min + 1; - _Enum() = delete; - constexpr _Enum(_Enumerated constant) : _value(constant) { } - - constexpr _Integral to_integral() const + constexpr static Integral to_integral(Enumerated value) { - return _value; + return (Integral)value; } - constexpr static const _Enum _from_integral(_Integral value) + constexpr static const Enumerated from_integral(Integral value) { return _value_array[_from_int_loop(value, true)]; } - constexpr static const _Enum _from_integral_unchecked(_Integral value) + constexpr static const Enumerated from_integral_unchecked(Integral value) { - return (_Enumerated)value; + return (Enumerated)value; } - const char* to_string() const + static const char* to_string(Enumerated value) { _processNames(); - for (size_t index = 0; index < _size; ++index) { - if (_value_array[index] == _value) + for (size_t index = 0; index < size; ++index) { + if (_value_array[index] == value) return _processedNames[index]; } throw std::domain_error("Enum::_to_string: invalid enum value"); } - constexpr static const _Enum _from_string(const char *name) + constexpr static const Enumerated from_string(const char *name) { return _value_array[_from_string_loop(name, true)]; } - constexpr static const _Enum _from_string_nocase(const char *name) + constexpr static const Enumerated from_string_nocase(const char *name) { return _value_array[_from_string_nocase_loop(name, true)]; } - constexpr static bool _is_valid(_Integral value) + constexpr static bool is_valid(Integral value) { return _from_int_loop(value, false) != _ENUM_NOT_FOUND; } - constexpr static bool _is_valid(const char *name) + 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) + constexpr static bool is_valid_nocase(const char *name) { return _from_string_nocase_loop(name, false) != _ENUM_NOT_FOUND; } - constexpr operator _Enumerated() const { return _value; } - protected: - _Enumerated _value; - static const char * const *_processedNames; static void _processNames() { if (_processedNames == nullptr) - _processedNames = _enum::_processNames(_name_array, _size); + _processedNames = _enum::_processNames(_name_array, size); } static const char* _getProcessedName(size_t index) @@ -516,28 +511,30 @@ class _Enum : public _GeneratedArrays { return _processedNames[index]; } - using _ValueIterable = _Iterable<_Enum, _ValueIterator<_Enum>>; - using _NameIterable = _Iterable<_Enum, _NameIterator<_Enum>>; + using _ValueIterable = + _enum::_Iterable>; + using _NameIterable = + _enum::_Iterable>; - friend _ValueIterator<_Enum>; - friend _NameIterator<_Enum>; + friend _enum::_ValueIterator; + friend _enum::_NameIterator; public: - static const _ValueIterable _values; - static const _NameIterable _names; + static const _ValueIterable values; + static const _NameIterable names; protected: - constexpr static size_t _from_int_loop(_Integral value, + constexpr static size_t _from_int_loop(Integral value, bool throw_exception, size_t index = 0) { return - index == _size ? + index == size ? (throw_exception ? throw std::runtime_error( - "Enum::_from_integral: invalid integer value") : + "enum_::traits::from_integral: invalid integer value") : _ENUM_NOT_FOUND) : - _value_array[index] == value ? index : + (Integral)_value_array[index] == value ? index : _from_int_loop(value, throw_exception, index + 1); } @@ -546,12 +543,12 @@ class _Enum : public _GeneratedArrays { size_t index = 0) { return - index == _size ? + index == size ? (throw_exception ? throw std::runtime_error( - "Enum::_from_string: invalid string argument") : + "enum_::traits::from_string: invalid string argument") : _ENUM_NOT_FOUND) : - _namesMatch(_name_array[index], name) ? index : + _enum::_namesMatch(_name_array[index], name) ? index : _from_string_loop(name, throw_exception, index + 1); } @@ -560,98 +557,78 @@ class _Enum : public _GeneratedArrays { size_t index = 0) { return - index == _size ? + index == size ? (throw_exception ? throw std::runtime_error( - "Enum::_from_string_nocase: invalid string argument") : + "enum::traits::_from_string_nocase: " + "invalid string argument") : _ENUM_NOT_FOUND) : - _namesMatchNocase(_name_array[index], name) ? index : + _enum::_namesMatchNocase(_name_array[index], name) ? index : _from_string_nocase_loop(name, throw_exception, index + 1); } - - public: - constexpr bool operator ==(const _Enum &other) const - { return _value == other._value; } - constexpr bool operator ==(const _Enumerated value) const - { return _value == value; } - template bool operator ==(T other) const = delete; - - constexpr bool operator !=(const _Enum &other) const - { return !(*this == other); } - constexpr bool operator !=(const _Enumerated value) const - { return !(*this == value); } - template bool operator !=(T other) const = delete; - - constexpr bool operator <(const _Enum &other) const - { return _value < other._value; } - constexpr bool operator <(const _Enumerated value) const - { return _value < value; } - template bool operator <(T other) const = delete; - - constexpr bool operator <=(const _Enum &other) const - { return _value <= other._value; } - constexpr bool operator <=(const _Enumerated value) const - { return _value <= value; } - template bool operator <=(T other) const = delete; - - constexpr bool operator >(const _Enum &other) const - { return _value > other._value; } - constexpr bool operator >(const _Enumerated value) const - { return _value > value; } - template bool operator >(T other) const = delete; - - constexpr bool operator >=(const _Enum &other) const - { return _value >= other._value; } - constexpr bool operator >=(const _Enumerated value) const - { return _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; }; -#define _ENUM_GLOBALS(EnumType, Tag) \ +template +constexpr typename traits::Integral to_integral(Enum value) +{ + return traits::to_integral(value); +} + +template +constexpr Enum from_integral(typename traits::Integral value) +{ + return traits::from_integral(value); +} + +template +constexpr Enum from_integral_unchecked(typename traits::Integral value) +{ + return traits::from_integral_unchecked(value); +} + +template +const char* to_string(Enum value) +{ + return traits::to_string(value); +} + +template +constexpr Enum from_string(const char *name) +{ + return traits::from_string(name); +} + +template +constexpr Enum from_string_nocase(const char *name) +{ + return traits::from_string_nocase(name); +} + +} // namespace enum_ + +#define _ENUM_GLOBALS(EnumType) \ namespace _enum { \ \ - constexpr const EnumType operator +(EnumType::_Enumerated enumerated) \ - { return (EnumType)enumerated; } \ + constexpr _GeneratedArrays::Enumerated _ENUM_WEAK \ + _GeneratedArrays::_value_array[]; \ + \ + constexpr const char * _ENUM_WEAK \ + _GeneratedArrays::_name_array[]; \ + \ + } \ \ template <> \ - constexpr EnumType::_ValueIterable _ENUM_WEAK EnumType::_values{}; \ + constexpr enum_::traits::_ValueIterable _ENUM_WEAK \ + enum_::traits::values{}; \ \ template <> \ - constexpr EnumType::_NameIterable _ENUM_WEAK EnumType::_names{}; \ - \ - constexpr _GeneratedArrays::_Enumerated _ENUM_WEAK \ - _GeneratedArrays::_value_array[]; \ - \ - constexpr const char * _ENUM_WEAK _GeneratedArrays::_name_array[]; \ + constexpr enum_::traits::_NameIterable _ENUM_WEAK \ + enum_::traits::names{}; \ \ template <> \ - const char * const * _ENUM_WEAK EnumType::_processedNames = nullptr; \ - \ - } - -} // namespace _enum + const char * const * _ENUM_WEAK enum_::traits::_processedNames = \ + nullptr; #define ENUM(EnumType, Integral, ...) \ - _ENUM_TAG_DECLARATION(EnumType); \ - _ENUM_ARRAYS(EnumType, Integral, _ENUM_TAG(EnumType), __VA_ARGS__); \ - using EnumType = _enum::_Enum<_enum::_ENUM_TAG(EnumType)>; \ - _ENUM_GLOBALS(EnumType, _ENUM_TAG(EnumType)); + _ENUM_ARRAYS(EnumType, Integral, __VA_ARGS__); \ + _ENUM_GLOBALS(EnumType); diff --git a/samples/1-basic.cc b/samples/1-basic.cc index f16be45..845de15 100644 --- a/samples/1-basic.cc +++ b/samples/1-basic.cc @@ -3,15 +3,16 @@ #include #include -ENUM(Channel, uint8_t, Red, Green = 2, Blue, Alias = Red); +ENUM(Channel, uint8_t, Red, Green = 2, Blue, Alias = Channel::Red); void print_channel(Channel channel) { std::cout << "channel \'" - << channel.to_string() + << enum_::to_string(channel) << "\' has value " - << (int)channel.to_integral() << std::endl; + << enum_::to_integral(channel) + << std::endl; } int main() @@ -28,32 +29,32 @@ int main() // Conversions from strings and the integral type. Function names are // prefixed with _ to avoid conflicts with constant names. _from_integral is // a checked cast. - channel = Channel::_from_integral(0); + channel = enum_::from_integral(0); print_channel(channel); - channel = Channel::_from_string("Blue"); + channel = enum_::from_string("Blue"); print_channel(channel); - channel = Channel::_from_string_nocase("bluE"); + channel = enum_::from_string_nocase("bluE"); print_channel(channel); // Bad conversions. try { - channel = Channel::_from_integral(15); + channel = enum_::from_integral(15); throw std::logic_error("expected an exception"); } catch (const std::runtime_error &e) { } try { - channel = Channel::_from_string("Purple"); + channel = enum_::from_string("Purple"); throw std::logic_error("expected an exception"); } catch (const std::runtime_error &e) { } try { - channel = Channel::_from_string_nocase("bluee"); + channel = enum_::from_string_nocase("bluee"); throw std::logic_error("expected an exception"); } catch (const std::runtime_error &e) { } @@ -61,29 +62,23 @@ int main() // Unsafe unchecked cast. - channel = Channel::_from_integral_unchecked(2); + channel = enum_::from_integral_unchecked(2); // Direct conversion of a constant unfortunately requires an explicit // promotion. - std::cout << (+Channel::Green).to_string() << std::endl; + std::cout << enum_::to_string(Channel::Green) << std::endl; // The type name is available as a string. - std::cout << Channel::_name << std::endl; + std::cout << enum_::traits::name << std::endl; return 0; } -static_assert(sizeof(Channel) == sizeof(uint8_t), - "enum has the same size as its underlying integral type"); - -static_assert(alignof(Channel) == alignof(uint8_t), - "enum has the same alignment as its underlying integral type"); - -static_assert(std::is_same(), +static_assert(std::is_same::Integral, uint8_t>(), "the underlying integral type is accessible as a member"); diff --git a/samples/2-iterate.cc b/samples/2-iterate.cc index c58ca53..a519399 100644 --- a/samples/2-iterate.cc +++ b/samples/2-iterate.cc @@ -8,22 +8,26 @@ ENUM(Channel, int, Red = 3, Green = 4, Blue = 0); int main() { // Static range properties. - std::cout << "first: " << (+Channel::_first).to_string() << std::endl; - std::cout << "last: " << (+Channel::_last).to_string() << std::endl; - std::cout << "minimum: " << (+Channel::_min).to_string() << std::endl; - std::cout << "maximum: " << (+Channel::_max).to_string() << std::endl; - std::cout << "count: " << Channel::_size << std::endl; - std::cout << "span: " << Channel::_span << std::endl; + std::cout << "first: " << + enum_::to_string(enum_::traits::first) << std::endl; + std::cout << "last: " << + enum_::to_string(enum_::traits::last) << std::endl; + std::cout << "minimum: " << + enum_::to_string(enum_::traits::min) << std::endl; + std::cout << "maximum: " << + enum_::to_string(enum_::traits::max) << std::endl; + std::cout << "count: " << enum_::traits::size << std::endl; + std::cout << "span: " << enum_::traits::span << std::endl; // Listing declared values. - for (Channel channel : Channel::_values) - std::cout << channel.to_integral() << " "; + for (Channel channel : enum_::traits::values) + std::cout << enum_::to_integral(channel) << " "; std::cout << std::endl; // Listing declared names. - for (const char *name : Channel::_names) + for (const char *name : enum_::traits::names) std::cout << name << " "; std::cout << std::endl; @@ -32,7 +36,7 @@ int main() // Direct iterator usage. std::cout << "first (using iterator): " - << *Channel::_names.begin() + << *enum_::traits::names.begin() << std::endl; return 0; diff --git a/samples/4-constexpr.cc b/samples/4-constexpr.cc index 9cbed71..5261568 100644 --- a/samples/4-constexpr.cc +++ b/samples/4-constexpr.cc @@ -10,37 +10,40 @@ ENUM(Channel, int, Red, Green, Blue); // Initialization. constexpr Channel channel_1 = Channel::Green; -constexpr Channel channel_4 = Channel::_from_integral(2); +constexpr Channel channel_4 = enum_::from_integral(2); -constexpr Channel channel_2 = Channel::_from_string("Blue"); -constexpr Channel channel_3 = Channel::_from_string_nocase("gReEn"); +constexpr Channel channel_2 = enum_::from_string("Blue"); +constexpr Channel channel_3 = enum_::from_string_nocase("gReEn"); // Conversion to integer (but not to string). -constexpr int channel_1_representation = channel_1.to_integral(); +constexpr int channel_1_representation = enum_::to_integral(channel_1); // Validity checks (including against strings). -constexpr bool should_be_valid_1 = Channel::_is_valid(2); -constexpr bool should_be_invalid_1 = Channel::_is_valid(42); +constexpr bool should_be_valid_1 = enum_::traits::is_valid(2); +constexpr bool should_be_invalid_1 = enum_::traits::is_valid(42); -constexpr bool should_be_valid_2 = Channel::_is_valid("Red"); -constexpr bool should_be_invalid_2 = Channel::_is_valid("red"); +constexpr bool should_be_valid_2 = enum_::traits::is_valid("Red"); +constexpr bool should_be_invalid_2 = + enum_::traits::is_valid("red"); -constexpr bool should_be_valid_3 = Channel::_is_valid_nocase("red"); -constexpr bool should_be_invalid_3 = Channel::_is_valid_nocase("reed"); +constexpr bool should_be_valid_3 = + enum_::traits::is_valid_nocase("red"); +constexpr bool should_be_invalid_3 = + enum_::traits::is_valid_nocase("reed"); // _names and _values collections and iterator creation. -constexpr Channel channel_5 = *Channel::_values.begin(); -constexpr auto name_iterator = Channel::_names.begin(); +constexpr Channel channel_5 = *enum_::traits::values.begin(); +constexpr auto name_iterator = enum_::traits::names.begin(); // Range properties. -constexpr Channel channel_6 = Channel::_max; -constexpr size_t span = Channel::_span; +constexpr Channel channel_6 = enum_::traits::max; +constexpr size_t span = enum_::traits::span; // Type name. -constexpr auto name = Channel::_name; +constexpr auto name = enum_::traits::name; // Explicit promotion. -constexpr int converted = (+Channel::Green).to_integral(); +constexpr int converted = enum_::to_integral(Channel::Green); @@ -51,7 +54,7 @@ void print_channel(int number, Channel channel) << "channel_" << number << " is " - << channel.to_string() + << enum_::to_string(channel) << std::endl; } diff --git a/samples/5-containers.cc b/samples/5-containers.cc index aa0236b..d723ce8 100644 --- a/samples/5-containers.cc +++ b/samples/5-containers.cc @@ -19,7 +19,7 @@ int main() vector.push_back(Channel::Red); for (Channel channel : vector) - std::cout << channel.to_string() << " "; + std::cout << enum_::to_string(channel) << " "; std::cout << std::endl; @@ -30,8 +30,8 @@ int main() std::map map = {{"first", Channel::Blue}}; map.insert({"second", Channel::Green}); - for (Channel channel : Channel::_values) - map.insert({channel.to_string(), channel}); + for (Channel channel : enum_::traits::values) + map.insert({enum_::to_string(channel), channel}); bool first = true; for (auto item : map) { @@ -43,7 +43,7 @@ int main() std::cout << item.first << " -> " - << item.second.to_string(); + << enum_::to_string(item.second); } std::cout << std::endl; diff --git a/samples/6-traits.cc b/samples/6-traits.cc index 6933025..f16ca8a 100644 --- a/samples/6-traits.cc +++ b/samples/6-traits.cc @@ -7,7 +7,7 @@ template constexpr const Enum default_() { - return Enum::_first; + return enum_::traits::first; } // Make it possible to override the convention for specific enums. @@ -35,10 +35,10 @@ int main() // default value is still declared in one place, not all over the program // code. Depth depth = default_(); - std::cout << depth.to_string() << std::endl; + std::cout << enum_::to_string(depth) << std::endl; - std::cout << default_().to_string() << std::endl; - std::cout << default_().to_string() << std::endl; + std::cout << enum_::to_string(default_()) << std::endl; + std::cout << enum_::to_string(default_()) << std::endl; return 0; }