diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 498b814..81d5c53 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -7,4 +7,4 @@ Thanks to: Tony Van Eerd Ben Alex -SlashLife (freenode) +Simon Stienen diff --git a/enum.h b/enum.h index 7a7d739..745b63e 100644 --- a/enum.h +++ b/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 +// in C++98. , this template could be replaced with a +// combination of std::conditional and std::is_integral. +template +struct representation { typedef typename T::integral_representation type; }; + +template <> struct representation { typedef bool type; }; +template <> struct representation { typedef char type; }; +template <> struct representation { typedef wchar_t type; }; +template <> struct representation { typedef signed char type; }; +template <> struct representation + { typedef unsigned char type; }; +template <> struct representation { typedef short type; }; +template <> struct representation + { typedef unsigned short type; }; +template <> struct representation { typedef int type; }; +template <> struct representation { typedef unsigned int type; }; +template <> struct representation { typedef long type; }; +template <> struct representation + { typedef unsigned long type; }; + +#ifdef BETTER_ENUMS__HAVE_LONG_LONG + +template <> struct representation { typedef long long type; }; +template <> struct representation + { typedef unsigned long long type; }; + +#endif + +#ifdef BETTER_ENUMS__HAVE_NEW_CHAR_TYPES + +template <> struct representation { typedef char16_t type; }; +template <> struct representation { typedef char32_t type; }; + +#endif + +template +struct underlying_traits { + typedef typename representation::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 _optional; \ - typedef ::better_enums::optional _optional_index; \ + typedef ::better_enums::optional _optional; \ + typedef ::better_enums::optional _optional_index; \ + typedef ::better_enums::underlying_traits _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 _value_iterable; \ - typedef ::better_enums::_Iterable _name_iterable; \ + typedef ::better_enums::_Iterable _value_iterable; \ + typedef ::better_enums::_Iterable _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(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(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( \ 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 \ + ::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::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::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 diff --git a/test/cxxtest/tests.h b/test/cxxtest/general.h similarity index 100% rename from test/cxxtest/tests.h rename to test/cxxtest/general.h diff --git a/test/cxxtest/underlying.h b/test/cxxtest/underlying.h new file mode 100644 index 0000000..2f95441 --- /dev/null +++ b/test/cxxtest/underlying.h @@ -0,0 +1,154 @@ +#include +#include + +// 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 { + 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); + } +};