mirror of
https://github.com/aantron/better-enums.git
synced 2025-12-08 01:36:44 +08:00
Experimental generalization of underlying types.
With this change, the underlying type can be a non-integral type that provides conversions to and from an integral type. See the test at test/cxxtest/underlying.h for some examples - though they are more verbose than strictly necessary, for testing needs. Move constructors in underlying types are not supported. It has been difficult so far to get constexpr code not to select the move constructor, which is generally not constexpr, for various operations.
This commit is contained in:
parent
b037d8b5eb
commit
4314ad3fd3
@ -7,4 +7,4 @@ Thanks to:
|
||||
|
||||
Tony Van Eerd
|
||||
Ben Alex
|
||||
SlashLife (freenode)
|
||||
Simon Stienen
|
||||
|
||||
256
enum.h
256
enum.h
@ -14,26 +14,43 @@
|
||||
|
||||
|
||||
|
||||
#ifdef BETTER_ENUMS_CONSTEXPR
|
||||
# define BETTER_ENUMS__HAVE_CONSTEXPR
|
||||
#else
|
||||
# ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# if __has_feature(cxx_constexpr)
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# if __has_feature(cxx_constexpr)
|
||||
# define BETTER_ENUMS__HAVE_CONSTEXPR
|
||||
# endif
|
||||
# if (__clang_major__ > 2) || \
|
||||
(__clang_major__ == 2) && (__clang_minor__ >= 9)
|
||||
# define BETTER_ENUMS__HAVE_LONG_LONG
|
||||
# define BETTER_ENUMS__HAVE_NEW_CHAR_TYPES
|
||||
# endif
|
||||
# else
|
||||
# if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L
|
||||
# if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6))
|
||||
# define BETTER_ENUMS__HAVE_CONSTEXPR
|
||||
# endif
|
||||
# else
|
||||
# if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6))
|
||||
# if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L
|
||||
# define BETTER_ENUMS__HAVE_CONSTEXPR
|
||||
# endif
|
||||
# if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3))
|
||||
# define BETTER_ENUMS__HAVE_LONG_LONG
|
||||
# endif
|
||||
# if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4))
|
||||
# define BETTER_ENUMS__HAVE_NEW_CHAR_TYPES
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# if _MSC_VER >= 1600
|
||||
# define BETTER_ENUMS__HAVE_LONG_LONG
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef BETTER_ENUMS_CONSTEXPR
|
||||
# define BETTER_ENUMS__HAVE_CONSTEXPR
|
||||
#endif
|
||||
|
||||
#ifdef BETTER_ENUMS_NO_CONSTEXPR
|
||||
# if defined(BETTER_ENUMS__HAVE_CONSTEXPR)
|
||||
# ifdef BETTER_ENUMS__HAVE_CONSTEXPR
|
||||
# undef BETTER_ENUMS__HAVE_CONSTEXPR
|
||||
# endif
|
||||
#endif
|
||||
@ -54,10 +71,6 @@
|
||||
# define BETTER_ENUMS__NULLPTR NULL
|
||||
#endif
|
||||
|
||||
#ifndef __GNUC__
|
||||
# define strcasecmp stricmp
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef BETTER_ENUMS_MACRO_FILE
|
||||
@ -413,6 +426,56 @@ inline void _trim_names(const char * const *raw_names,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This template is unfortunately necessary (?) due to the lack of <type_traits>
|
||||
// in C++98. <With type_traits>, this template could be replaced with a
|
||||
// combination of std::conditional and std::is_integral.
|
||||
template <typename T>
|
||||
struct representation { typedef typename T::integral_representation type; };
|
||||
|
||||
template <> struct representation<bool> { typedef bool type; };
|
||||
template <> struct representation<char> { typedef char type; };
|
||||
template <> struct representation<wchar_t> { typedef wchar_t type; };
|
||||
template <> struct representation<signed char> { typedef signed char type; };
|
||||
template <> struct representation<unsigned char>
|
||||
{ typedef unsigned char type; };
|
||||
template <> struct representation<short> { typedef short type; };
|
||||
template <> struct representation<unsigned short>
|
||||
{ typedef unsigned short type; };
|
||||
template <> struct representation<int> { typedef int type; };
|
||||
template <> struct representation<unsigned int> { typedef unsigned int type; };
|
||||
template <> struct representation<long> { typedef long type; };
|
||||
template <> struct representation<unsigned long>
|
||||
{ typedef unsigned long type; };
|
||||
|
||||
#ifdef BETTER_ENUMS__HAVE_LONG_LONG
|
||||
|
||||
template <> struct representation<long long> { typedef long long type; };
|
||||
template <> struct representation<unsigned long long>
|
||||
{ typedef unsigned long long type; };
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef BETTER_ENUMS__HAVE_NEW_CHAR_TYPES
|
||||
|
||||
template <> struct representation<char16_t> { typedef char16_t type; };
|
||||
template <> struct representation<char32_t> { typedef char32_t type; };
|
||||
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
struct underlying_traits {
|
||||
typedef typename representation<T>::type integral_representation;
|
||||
|
||||
BETTER_ENUMS__CONSTEXPR static integral_representation
|
||||
to_integral(const T &v) { return (integral_representation)v; }
|
||||
BETTER_ENUMS__CONSTEXPR static T
|
||||
from_integral(integral_representation n) { return T(n); }
|
||||
BETTER_ENUMS__CONSTEXPR static bool
|
||||
are_equal(const T& u, const T& v) { return u == v; }
|
||||
};
|
||||
|
||||
} // namespace better_enums
|
||||
|
||||
|
||||
@ -469,35 +532,67 @@ constexpr const char *_final_ ## index = \
|
||||
|
||||
|
||||
|
||||
// TODO Convert integral to underlying only at the last possible moment, if the
|
||||
// integral value is valid. This should prevent unexpected behavior. For this to
|
||||
// work, the underlying values array must store the integral equivalents. That
|
||||
// would also eliminate the need for "are_equal", but would increase overhead
|
||||
// during iteration.
|
||||
|
||||
// TODO Choose the right return type semantics once the _values array
|
||||
// representation is chosen: values if integral, const references if underlying.
|
||||
|
||||
#define BETTER_ENUMS__TYPE(SetUnderlyingType, SwitchType, GenerateSwitchType, \
|
||||
GenerateStrings, ToStringConstexpr, \
|
||||
DeclareInitialize, DefineInitialize, CallInitialize,\
|
||||
Enum, Integral, ...) \
|
||||
Enum, Underlying, ...) \
|
||||
\
|
||||
namespace better_enums { \
|
||||
namespace _data_ ## Enum { \
|
||||
\
|
||||
BETTER_ENUMS__ID(GenerateSwitchType(Integral, __VA_ARGS__)) \
|
||||
BETTER_ENUMS__ID(GenerateSwitchType(Underlying, __VA_ARGS__)) \
|
||||
\
|
||||
} \
|
||||
} \
|
||||
\
|
||||
class Enum { \
|
||||
private: \
|
||||
typedef ::better_enums::optional<Enum> _optional; \
|
||||
typedef ::better_enums::optional<std::size_t> _optional_index; \
|
||||
typedef ::better_enums::optional<Enum> _optional; \
|
||||
typedef ::better_enums::optional<std::size_t> _optional_index; \
|
||||
typedef ::better_enums::underlying_traits<Underlying> _traits; \
|
||||
\
|
||||
public: \
|
||||
enum _enumerated SetUnderlyingType(Integral) { __VA_ARGS__ }; \
|
||||
typedef Integral _integral; \
|
||||
typedef Underlying _underlying; \
|
||||
typedef _traits::integral_representation _integral; \
|
||||
\
|
||||
BETTER_ENUMS__CONSTEXPR Enum(_enumerated value) : _value(value) { } \
|
||||
enum _enumerated SetUnderlyingType(Underlying) { __VA_ARGS__ }; \
|
||||
\
|
||||
BETTER_ENUMS__CONSTEXPR Enum(_enumerated value) : \
|
||||
_value(_traits::from_integral(value)) { } \
|
||||
\
|
||||
BETTER_ENUMS__CONSTEXPR operator SwitchType(Enum)() const \
|
||||
{ \
|
||||
return (SwitchType(Enum))_value; \
|
||||
return (SwitchType(Enum))_traits::to_integral(_value); \
|
||||
} \
|
||||
\
|
||||
_underlying& operator *() { return _value; } \
|
||||
BETTER_ENUMS__CONSTEXPR const _underlying& operator *() const \
|
||||
{ return _value; } \
|
||||
\
|
||||
_underlying* operator ->() { return &_value; } \
|
||||
BETTER_ENUMS__CONSTEXPR const _underlying* operator ->() const \
|
||||
{ return &_value; } \
|
||||
\
|
||||
_underlying _to_underlying() { return _value; } \
|
||||
BETTER_ENUMS__CONSTEXPR const _underlying& _to_underlying() const \
|
||||
{ return _value; } \
|
||||
\
|
||||
BETTER_ENUMS__CONSTEXPR static Enum \
|
||||
_from_underlying(const _underlying &value); \
|
||||
BETTER_ENUMS__CONSTEXPR static Enum \
|
||||
_from_underlying_unchecked(const _underlying &value); \
|
||||
BETTER_ENUMS__CONSTEXPR static _optional \
|
||||
_from_underlying_nothrow(const _underlying &value); \
|
||||
\
|
||||
BETTER_ENUMS__CONSTEXPR _integral _to_integral() const; \
|
||||
BETTER_ENUMS__CONSTEXPR static Enum _from_integral(_integral value); \
|
||||
BETTER_ENUMS__CONSTEXPR static Enum \
|
||||
@ -514,15 +609,15 @@ class Enum { \
|
||||
BETTER_ENUMS__CONSTEXPR static _optional \
|
||||
_from_string_nocase_nothrow(const char *name); \
|
||||
\
|
||||
BETTER_ENUMS__CONSTEXPR static bool _is_valid(_integral value); \
|
||||
BETTER_ENUMS__CONSTEXPR static bool _is_valid(const _underlying &value); \
|
||||
BETTER_ENUMS__CONSTEXPR static bool _is_valid(const char *name); \
|
||||
BETTER_ENUMS__CONSTEXPR static bool _is_valid_nocase(const char *name); \
|
||||
\
|
||||
typedef ::better_enums::_Iterable<Enum> _value_iterable; \
|
||||
typedef ::better_enums::_Iterable<const char*> _name_iterable; \
|
||||
typedef ::better_enums::_Iterable<Enum> _value_iterable; \
|
||||
typedef ::better_enums::_Iterable<const char*> _name_iterable; \
|
||||
\
|
||||
typedef _value_iterable::iterator _value_iterator; \
|
||||
typedef _name_iterable::iterator _name_iterator; \
|
||||
typedef _value_iterable::iterator _value_iterator; \
|
||||
typedef _name_iterable::iterator _name_iterator; \
|
||||
\
|
||||
BETTER_ENUMS__CONSTEXPR static const std::size_t _size = \
|
||||
BETTER_ENUMS__ID(BETTER_ENUMS__PP_COUNT(__VA_ARGS__)); \
|
||||
@ -531,15 +626,18 @@ class Enum { \
|
||||
BETTER_ENUMS__CONSTEXPR static _value_iterable _values(); \
|
||||
ToStringConstexpr static _name_iterable _names(); \
|
||||
\
|
||||
_integral _value; \
|
||||
_underlying _value; \
|
||||
\
|
||||
private: \
|
||||
Enum() : _value(0) { } \
|
||||
Enum() : _value(_traits::from_integral(0)) { } \
|
||||
\
|
||||
explicit BETTER_ENUMS__CONSTEXPR Enum(const _underlying &value) : \
|
||||
_value(value) { } \
|
||||
\
|
||||
DeclareInitialize \
|
||||
\
|
||||
BETTER_ENUMS__CONSTEXPR static _optional_index \
|
||||
_from_int_loop(_integral value, std::size_t index = 0); \
|
||||
_from_value_loop(const _underlying &value, std::size_t index = 0); \
|
||||
BETTER_ENUMS__CONSTEXPR static _optional_index \
|
||||
_from_string_loop(const char *name, std::size_t index = 0); \
|
||||
BETTER_ENUMS__CONSTEXPR static _optional_index \
|
||||
@ -565,18 +663,40 @@ operator +(Enum::_enumerated enumerated) \
|
||||
return (Enum)enumerated; \
|
||||
} \
|
||||
\
|
||||
BETTER_ENUMS__CONSTEXPR inline Enum::_integral Enum::_to_integral() const \
|
||||
BETTER_ENUMS__CONSTEXPR inline Enum \
|
||||
Enum::_from_underlying(const _underlying &value) \
|
||||
{ \
|
||||
return _value; \
|
||||
return \
|
||||
::better_enums::_or_throw(_from_underlying_nothrow(value), \
|
||||
"Enum::_from_underlying: invalid argument"); \
|
||||
} \
|
||||
\
|
||||
BETTER_ENUMS__CONSTEXPR inline Enum \
|
||||
Enum::_from_integral_unchecked(Enum::_integral value) \
|
||||
Enum::_from_underlying_unchecked(const _underlying &value) \
|
||||
{ \
|
||||
return Enum(value); \
|
||||
} \
|
||||
\
|
||||
BETTER_ENUMS__CONSTEXPR inline Enum::_optional \
|
||||
Enum::_from_underlying_nothrow(const _underlying &value) \
|
||||
{ \
|
||||
return \
|
||||
::better_enums::_map_index<Enum>(BETTER_ENUMS__NS(Enum)::value_array, \
|
||||
_from_value_loop(value)); \
|
||||
} \
|
||||
\
|
||||
BETTER_ENUMS__CONSTEXPR inline Enum::_integral Enum::_to_integral() const \
|
||||
{ \
|
||||
return _traits::to_integral(_value); \
|
||||
} \
|
||||
\
|
||||
BETTER_ENUMS__CONSTEXPR inline Enum \
|
||||
Enum::_from_integral_unchecked(_integral value) \
|
||||
{ \
|
||||
return (_enumerated)value; \
|
||||
} \
|
||||
\
|
||||
BETTER_ENUMS__CONSTEXPR inline Enum Enum::_from_integral(Enum::_integral value)\
|
||||
BETTER_ENUMS__CONSTEXPR inline Enum Enum::_from_integral(_integral value) \
|
||||
{ \
|
||||
return \
|
||||
::better_enums::_or_throw(_from_integral_nothrow(value), \
|
||||
@ -584,11 +704,9 @@ BETTER_ENUMS__CONSTEXPR inline Enum Enum::_from_integral(Enum::_integral value)\
|
||||
} \
|
||||
\
|
||||
BETTER_ENUMS__CONSTEXPR inline Enum::_optional \
|
||||
Enum::_from_integral_nothrow(Enum::_integral value) \
|
||||
Enum::_from_integral_nothrow(_integral value) \
|
||||
{ \
|
||||
return \
|
||||
::better_enums::_map_index<Enum>(BETTER_ENUMS__NS(Enum)::value_array, \
|
||||
_from_int_loop(value)); \
|
||||
return _from_underlying_nothrow(_traits::from_integral(value)); \
|
||||
} \
|
||||
\
|
||||
ToStringConstexpr inline const char* Enum::_to_string() const \
|
||||
@ -597,7 +715,7 @@ ToStringConstexpr inline const char* Enum::_to_string() const \
|
||||
::better_enums::_or_throw( \
|
||||
::better_enums::_map_index<const char*>( \
|
||||
BETTER_ENUMS__NS(Enum)::name_array(), \
|
||||
_from_int_loop(CallInitialize(_value))), \
|
||||
_from_value_loop(CallInitialize(_value))), \
|
||||
"Enum::to_string: invalid enum value"); \
|
||||
} \
|
||||
\
|
||||
@ -632,9 +750,9 @@ Enum::_from_string_nocase_nothrow(const char *name) \
|
||||
_from_string_nocase_loop(name)); \
|
||||
} \
|
||||
\
|
||||
BETTER_ENUMS__CONSTEXPR inline bool Enum::_is_valid(Enum::_integral value) \
|
||||
BETTER_ENUMS__CONSTEXPR inline bool Enum::_is_valid(const _underlying &value) \
|
||||
{ \
|
||||
return _from_int_loop(value); \
|
||||
return _from_value_loop(value); \
|
||||
} \
|
||||
\
|
||||
BETTER_ENUMS__CONSTEXPR inline bool Enum::_is_valid(const char *name) \
|
||||
@ -667,13 +785,14 @@ ToStringConstexpr inline Enum::_name_iterable Enum::_names() \
|
||||
DefineInitialize(Enum) \
|
||||
\
|
||||
BETTER_ENUMS__CONSTEXPR inline Enum::_optional_index \
|
||||
Enum::_from_int_loop(Enum::_integral value, std::size_t index) \
|
||||
Enum::_from_value_loop(const Enum::_underlying &value, std::size_t index) \
|
||||
{ \
|
||||
return \
|
||||
index == _size ? _optional_index() : \
|
||||
BETTER_ENUMS__NS(Enum)::value_array[index]._value == value ? \
|
||||
_optional_index(index) : \
|
||||
_from_int_loop(value, index + 1); \
|
||||
_traits::are_equal( \
|
||||
BETTER_ENUMS__NS(Enum)::value_array[index]._value, value) ? \
|
||||
_optional_index(index) : \
|
||||
_from_value_loop(value, index + 1); \
|
||||
} \
|
||||
\
|
||||
BETTER_ENUMS__CONSTEXPR inline Enum::_optional_index \
|
||||
@ -699,26 +818,23 @@ Enum::_from_string_nocase_loop(const char *name, std::size_t index) \
|
||||
} \
|
||||
\
|
||||
BETTER_ENUMS__CONSTEXPR inline bool operator ==(const Enum &a, const Enum &b) \
|
||||
{ return a._value == b._value; } \
|
||||
{ \
|
||||
return \
|
||||
::better_enums::underlying_traits<Enum::_underlying> \
|
||||
::are_equal(a._value, b._value); \
|
||||
} \
|
||||
\
|
||||
BETTER_ENUMS__CONSTEXPR inline bool operator !=(const Enum &a, const Enum &b) \
|
||||
{ return a._value != b._value; } \
|
||||
BETTER_ENUMS__CONSTEXPR inline bool operator <(const Enum &a, const Enum &b) \
|
||||
{ return a._value < b._value; } \
|
||||
BETTER_ENUMS__CONSTEXPR inline bool operator <=(const Enum &a, const Enum &b) \
|
||||
{ return a._value <= b._value; } \
|
||||
BETTER_ENUMS__CONSTEXPR inline bool operator >(const Enum &a, const Enum &b) \
|
||||
{ return a._value > b._value; } \
|
||||
BETTER_ENUMS__CONSTEXPR inline bool operator >=(const Enum &a, const Enum &b) \
|
||||
{ return a._value >= b._value; }
|
||||
{ return !(a == b); }
|
||||
|
||||
|
||||
|
||||
// C++98, C++11
|
||||
#define BETTER_ENUMS__CXX98_UNDERLYING_TYPE(Integral)
|
||||
#define BETTER_ENUMS__CXX98_UNDERLYING_TYPE(Underlying)
|
||||
|
||||
// C++11
|
||||
#define BETTER_ENUMS__CXX11_UNDERLYING_TYPE(Integral) \
|
||||
: Integral
|
||||
#define BETTER_ENUMS__CXX11_UNDERLYING_TYPE(Underlying) \
|
||||
: ::better_enums::underlying_traits<Underlying>::integral_representation
|
||||
|
||||
// C++98, C++11
|
||||
#define BETTER_ENUMS__REGULAR_ENUM_SWITCH_TYPE(Type) \
|
||||
@ -729,11 +845,13 @@ BETTER_ENUMS__CONSTEXPR inline bool operator >=(const Enum &a, const Enum &b) \
|
||||
BETTER_ENUMS__NS(Type)::EnumClassForSwitchStatements
|
||||
|
||||
// C++98, C++11
|
||||
#define BETTER_ENUMS__REGULAR_ENUM_SWITCH_TYPE_GENERATE(Integral, ...)
|
||||
#define BETTER_ENUMS__REGULAR_ENUM_SWITCH_TYPE_GENERATE(Underlying, ...)
|
||||
|
||||
// C++11
|
||||
#define BETTER_ENUMS__ENUM_CLASS_SWITCH_TYPE_GENERATE(Integral, ...) \
|
||||
enum class EnumClassForSwitchStatements : Integral { __VA_ARGS__ };
|
||||
#define BETTER_ENUMS__ENUM_CLASS_SWITCH_TYPE_GENERATE(Underlying, ...) \
|
||||
enum class EnumClassForSwitchStatements : \
|
||||
::better_enums::underlying_traits<Underlying>::integral_representation \
|
||||
{ __VA_ARGS__ };
|
||||
|
||||
// C++98
|
||||
#define BETTER_ENUMS__CXX98_TRIM_STRINGS_ARRAYS(Enum, ...) \
|
||||
@ -878,7 +996,7 @@ BETTER_ENUMS__CONSTEXPR inline bool operator >=(const Enum &a, const Enum &b) \
|
||||
BETTER_ENUMS__DO_CALL_INITIALIZE
|
||||
#endif
|
||||
|
||||
#define ENUM(Enum, Integral, ...) \
|
||||
#define ENUM(Enum, Underlying, ...) \
|
||||
BETTER_ENUMS__ID(BETTER_ENUMS__TYPE( \
|
||||
BETTER_ENUMS__CXX11_UNDERLYING_TYPE, \
|
||||
BETTER_ENUMS__DEFAULT_SWITCH_TYPE, \
|
||||
@ -888,9 +1006,9 @@ BETTER_ENUMS__CONSTEXPR inline bool operator >=(const Enum &a, const Enum &b) \
|
||||
BETTER_ENUMS__DEFAULT_DECLARE_INITIALIZE, \
|
||||
BETTER_ENUMS__DEFAULT_DEFINE_INITIALIZE, \
|
||||
BETTER_ENUMS__DEFAULT_CALL_INITIALIZE, \
|
||||
Enum, Integral, __VA_ARGS__))
|
||||
Enum, Underlying, __VA_ARGS__))
|
||||
|
||||
#define SLOW_ENUM(Enum, Integral, ...) \
|
||||
#define SLOW_ENUM(Enum, Underlying, ...) \
|
||||
BETTER_ENUMS__ID(BETTER_ENUMS__TYPE( \
|
||||
BETTER_ENUMS__CXX11_UNDERLYING_TYPE, \
|
||||
BETTER_ENUMS__DEFAULT_SWITCH_TYPE, \
|
||||
@ -900,11 +1018,11 @@ BETTER_ENUMS__CONSTEXPR inline bool operator >=(const Enum &a, const Enum &b) \
|
||||
BETTER_ENUMS__DO_NOT_DECLARE_INITIALIZE, \
|
||||
BETTER_ENUMS__DO_NOT_DEFINE_INITIALIZE, \
|
||||
BETTER_ENUMS__DO_NOT_CALL_INITIALIZE, \
|
||||
Enum, Integral, __VA_ARGS__))
|
||||
Enum, Underlying, __VA_ARGS__))
|
||||
|
||||
#else
|
||||
|
||||
#define ENUM(Enum, Integral, ...) \
|
||||
#define ENUM(Enum, Underlying, ...) \
|
||||
BETTER_ENUMS__ID(BETTER_ENUMS__TYPE( \
|
||||
BETTER_ENUMS__CXX98_UNDERLYING_TYPE, \
|
||||
BETTER_ENUMS__DEFAULT_SWITCH_TYPE, \
|
||||
@ -914,7 +1032,7 @@ BETTER_ENUMS__CONSTEXPR inline bool operator >=(const Enum &a, const Enum &b) \
|
||||
BETTER_ENUMS__DO_DECLARE_INITIALIZE, \
|
||||
BETTER_ENUMS__DO_DEFINE_INITIALIZE, \
|
||||
BETTER_ENUMS__DO_CALL_INITIALIZE, \
|
||||
Enum, Integral, __VA_ARGS__))
|
||||
Enum, Underlying, __VA_ARGS__))
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
154
test/cxxtest/underlying.h
Normal file
154
test/cxxtest/underlying.h
Normal file
@ -0,0 +1,154 @@
|
||||
#include <cxxtest/TestSuite.h>
|
||||
#include <enum.h>
|
||||
|
||||
// This test uses internal constants declared by feature testing macros in
|
||||
// enum.h. They are not part of the public interface, but are used here to avoid
|
||||
// duplication of definitions for C++11 and C++98.
|
||||
//
|
||||
// BETTER_ENUMS__CONSTEXPR is a synonym for constexpr when available, and
|
||||
// othewise expands to nothing.
|
||||
|
||||
|
||||
|
||||
// Using a type trait.
|
||||
struct html_color_1 {
|
||||
unsigned char r, g, b;
|
||||
|
||||
BETTER_ENUMS__CONSTEXPR
|
||||
html_color_1(unsigned char _r, unsigned char _g, unsigned char _b) :
|
||||
r(_r), g(_g), b(_b) { }
|
||||
};
|
||||
|
||||
// In C++11, can simply to struct ::better_enums::underlying_traits below.
|
||||
namespace better_enums {
|
||||
|
||||
template <>
|
||||
struct underlying_traits<html_color_1> {
|
||||
typedef unsigned int integral_representation;
|
||||
|
||||
BETTER_ENUMS__CONSTEXPR static html_color_1 from_integral(unsigned int i)
|
||||
{ return html_color_1(i >> 16 & 0xff, i >> 8 & 0xff, i & 0xff); }
|
||||
|
||||
BETTER_ENUMS__CONSTEXPR static unsigned int to_integral(html_color_1 v)
|
||||
{ return (unsigned int)v.r << 16 | (unsigned int)v.g << 8 | v.b; }
|
||||
|
||||
BETTER_ENUMS__CONSTEXPR static bool
|
||||
are_equal(const html_color_1 &u, const html_color_1 &v)
|
||||
{ return u.r == v.r && u.g == v.g && u.b == v.b; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
ENUM(HtmlColor1, html_color_1,
|
||||
darksalmon = 0xc47451, purplemimosa = 0x9e7bff, slimegreen = 0xbce954)
|
||||
|
||||
|
||||
|
||||
// Using member definitions.
|
||||
struct html_color_2 {
|
||||
unsigned char r, g, b;
|
||||
|
||||
BETTER_ENUMS__CONSTEXPR
|
||||
html_color_2(unsigned char _r, unsigned char _g, unsigned char _b) :
|
||||
r(_r), g(_g), b(_b) { }
|
||||
|
||||
|
||||
|
||||
typedef unsigned int integral_representation;
|
||||
|
||||
explicit BETTER_ENUMS__CONSTEXPR html_color_2(unsigned int i) :
|
||||
r(i >> 16 & 0xff), g(i >> 8 & 0xff), b(i & 0xff) { }
|
||||
|
||||
BETTER_ENUMS__CONSTEXPR operator unsigned int() const
|
||||
{ return (unsigned int)r << 16 | (unsigned int)g << 8 | b; }
|
||||
};
|
||||
|
||||
// Initializers are not supported before GCC 5.1, even with a literal type and
|
||||
// constexpr conversion to integer.
|
||||
#ifdef BETTER_ENUMS__HAVE_CONSTEXPR
|
||||
# ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# define SUPPORT_INITIALIZER
|
||||
# else
|
||||
# if __GNUC__ >= 5
|
||||
# define SUPPORT_INITIALIZER
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef SUPPORT_INITIALIZER
|
||||
ENUM(HtmlColor2, html_color_2,
|
||||
darksalmon = 0xc47451, purplemimosa = 0x9e7bff, slimegreen = 0xbce954,
|
||||
celeste = html_color_2(0x50, 0xeb, 0xec))
|
||||
#else
|
||||
ENUM(HtmlColor2, html_color_2,
|
||||
darksalmon = 0xc47451, purplemimosa = 0x9e7bff, slimegreen = 0xbce954)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
class UnderlyingTypeTests : public CxxTest::TestSuite {
|
||||
public:
|
||||
void test_basics_and_operators()
|
||||
{
|
||||
HtmlColor1 color_1 = HtmlColor1::darksalmon;
|
||||
|
||||
TS_ASSERT_EQUALS(color_1->r, 0xc4);
|
||||
TS_ASSERT_EQUALS(color_1->g, 0x74);
|
||||
TS_ASSERT_EQUALS(color_1->b, 0x51);
|
||||
|
||||
TS_ASSERT_EQUALS(sizeof(HtmlColor1), sizeof(html_color_1));
|
||||
|
||||
HtmlColor2 color_2 = HtmlColor2::purplemimosa;
|
||||
|
||||
TS_ASSERT_EQUALS(color_2->r, 0x9e);
|
||||
TS_ASSERT_EQUALS(color_2->g, 0x7b);
|
||||
TS_ASSERT_EQUALS(color_2->b, 0xff);
|
||||
|
||||
TS_ASSERT_EQUALS(sizeof(HtmlColor2), sizeof(html_color_2));
|
||||
|
||||
#ifdef SUPPORT_INITIALIZER
|
||||
HtmlColor2 color_3 = HtmlColor2::celeste;
|
||||
|
||||
TS_ASSERT_EQUALS(color_3->r, 0x50);
|
||||
TS_ASSERT_EQUALS(color_3->g, 0xeb);
|
||||
TS_ASSERT_EQUALS(color_3->b, 0xec);
|
||||
#endif
|
||||
|
||||
color_1->r = 0;
|
||||
|
||||
const HtmlColor1 const_color = HtmlColor1::slimegreen;
|
||||
TS_ASSERT_EQUALS(const_color->r, 0xbc);
|
||||
|
||||
html_color_1 &color_1_reference = *color_1;
|
||||
const html_color_1 &const_color_reference = *const_color;
|
||||
|
||||
TS_ASSERT_EQUALS(color_1_reference.r, 0);
|
||||
TS_ASSERT_EQUALS(const_color_reference.r, 0xbc);
|
||||
}
|
||||
|
||||
void test_conversion_functions()
|
||||
{
|
||||
HtmlColor2 color_1 = HtmlColor2::darksalmon;
|
||||
html_color_2 color_1_underlying = color_1._to_underlying();
|
||||
|
||||
TS_ASSERT_EQUALS(color_1_underlying.r, color_1->r);
|
||||
TS_ASSERT_EQUALS(color_1_underlying.g, color_1->g);
|
||||
TS_ASSERT_EQUALS(color_1_underlying.b, color_1->b);
|
||||
|
||||
color_1_underlying = html_color_2(HtmlColor2::slimegreen);
|
||||
color_1 = HtmlColor2::_from_underlying(color_1_underlying);
|
||||
|
||||
TS_ASSERT_EQUALS(color_1->r, color_1_underlying.r);
|
||||
TS_ASSERT_EQUALS(color_1->g, color_1_underlying.g);
|
||||
TS_ASSERT_EQUALS(color_1->b, color_1_underlying.b);
|
||||
|
||||
color_1_underlying = html_color_2(1, 2, 3);
|
||||
color_1 = HtmlColor2::_from_underlying_unchecked(color_1_underlying);
|
||||
|
||||
TS_ASSERT_EQUALS(color_1->r, color_1_underlying.r);
|
||||
TS_ASSERT_EQUALS(color_1->g, color_1_underlying.g);
|
||||
TS_ASSERT_EQUALS(color_1->b, color_1_underlying.b);
|
||||
}
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user