From 1b3d1cc7845c6c11d25e18c41f7bda9d6224e1b5 Mon Sep 17 00:00:00 2001 From: Anton Bachin Date: Mon, 18 May 2015 19:41:58 -0500 Subject: [PATCH] Forbade nearly all implicit conversions to integral types. Each Better Enum now has an internal enum class type to which it is convertible, instead of being convertible to the regular enum that defines its constants. switch statements are compiled at the enum class type. This comes at the price of the user having to type +Enum::Constant instead of Enum::Constant in cases, in order to trigger an explicit promotion of the pre-C++11 enum to Better Enum, so it can then be implicitly converted to the enum class. The remaining "hole" is that direct references to constants (Enum::Constant) are still implicitly convertible to integral types, because they have naked pre-C++11 enum type. --- enum.h | 20 ++++++-------------- example/3-switch.cc | 6 +++--- example/7-bitset.cc | 6 +++--- test/cxxtest/tests.h | 18 +++++++++++++----- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/enum.h b/enum.h index d1f61d3..bbbcde0 100644 --- a/enum.h +++ b/enum.h @@ -597,6 +597,8 @@ struct _Base { \ \ enum { __VA_ARGS__ }; \ \ +enum class _Case : Integral { __VA_ARGS__ }; \ + \ constexpr const _Base _value_array[] = \ { _ENUM_EAT_ASSIGN(_Base, __VA_ARGS__) }; \ \ @@ -618,19 +620,6 @@ constexpr const _Iterable _names{_name_array, _size}; \ #define _ENUM_NS(EnumType) _enum::_data_ ## EnumType -#ifndef BETTER_ENUMS_SAFER_SWITCH - -#define _ENUM_CONVERSION_FOR_SWITCH(Integral, ...) \ - constexpr operator _Enumerated() const { return _value; } - -#else - -#define _ENUM_CONVERSION_FOR_SWITCH(Integral, ...) \ - enum class _Case : Integral { __VA_ARGS__ }; \ - constexpr operator _Case() const { return (_Case)_value; } - -#endif // #ifndef BETTER_ENUMS_SAFER_SWITCH - #define _ENUM_TYPE(EnumType, Integral, ...) \ class EnumType : public _ENUM_NS(EnumType)::_Base { \ protected: \ @@ -727,7 +716,10 @@ class EnumType : public _ENUM_NS(EnumType)::_Base { \ return _from_string_nocase_loop(name); \ } \ \ - _ENUM_CONVERSION_FOR_SWITCH(Integral, __VA_ARGS__); \ + constexpr operator _ENUM_NS(EnumType)::_Case() const \ + { \ + return (_ENUM_NS(EnumType)::_Case)_value; \ + } \ \ constexpr static auto &_values = _ENUM_NS(EnumType)::_values; \ constexpr static auto &_names = _ENUM_NS(EnumType)::_names; \ diff --git a/example/3-switch.cc b/example/3-switch.cc index 7913b5b..dbab9f4 100644 --- a/example/3-switch.cc +++ b/example/3-switch.cc @@ -10,15 +10,15 @@ void respond_to_channel(Channel channel) // Try adding an extra case or removing one. Your compiler should issue a // warning. switch (channel) { - case Channel::Red: + case +Channel::Red: std::cout << "red channel" << std::endl; break; - case Channel::Green: + case +Channel::Green: std::cout << "green channel" << std::endl; break; - case Channel::Blue: + case +Channel::Blue: std::cout << "blue channel" << std::endl; break; diff --git a/example/7-bitset.cc b/example/7-bitset.cc index e643466..e1a35af 100644 --- a/example/7-bitset.cc +++ b/example/7-bitset.cc @@ -10,7 +10,7 @@ constexpr Enum maximum(Enum accumulator = Enum::_values[0], size_t index = 1) { return index >= Enum::_size ? accumulator : - Enum::_values[index] > accumulator ? + +Enum::_values[index] > accumulator ? maximum(+Enum::_values[index], index + 1) : maximum(accumulator, index + 1); } @@ -19,7 +19,7 @@ ENUM(Channel, int, Red, Green, Blue); int main() { - using ChannelSet = std::bitset() + 1>; + using ChannelSet = std::bitset().to_integral() + 1>; ChannelSet red_only; red_only.set(Channel::Red); @@ -33,7 +33,7 @@ int main() std::cout << channel.to_string() << " bit is set to " - << red_and_blue[channel] + << red_and_blue[channel.to_integral()] << std::endl; } diff --git a/test/cxxtest/tests.h b/test/cxxtest/tests.h index d691d35..7b96ef4 100644 --- a/test/cxxtest/tests.h +++ b/test/cxxtest/tests.h @@ -56,13 +56,21 @@ static_assert_1(!(std::is_constructible())); // Intended implicit conversions. static_assert_1((std::is_convertible())); -static_assert_1(!(std::is_convertible())); -static_assert_1(!(std::is_convertible())); -static_assert_1(!(std::is_convertible())); // Regrettable implicit conversions. -static_assert_1((std::is_convertible())); -static_assert_1((std::is_convertible())); +static_assert_1((std::is_convertible())); + +// Disallowed implicit conversions. +static_assert_1(!(std::is_convertible())); +static_assert_1(!(std::is_convertible())); +static_assert_1(!(std::is_convertible())); +static_assert_1(!(std::is_convertible())); +static_assert_1(!(std::is_convertible())); +static_assert_1(!(std::is_convertible())); +static_assert_1(!(std::is_convertible()));