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.
This commit is contained in:
Anton Bachin 2015-05-18 19:41:58 -05:00
parent 08dbe47edd
commit 1b3d1cc784
4 changed files with 25 additions and 25 deletions

20
enum.h
View File

@ -597,6 +597,8 @@ struct _Base { \
\ \
enum { __VA_ARGS__ }; \ enum { __VA_ARGS__ }; \
\ \
enum class _Case : Integral { __VA_ARGS__ }; \
\
constexpr const _Base _value_array[] = \ constexpr const _Base _value_array[] = \
{ _ENUM_EAT_ASSIGN(_Base, __VA_ARGS__) }; \ { _ENUM_EAT_ASSIGN(_Base, __VA_ARGS__) }; \
\ \
@ -618,19 +620,6 @@ constexpr const _Iterable<const char*> _names{_name_array, _size}; \
#define _ENUM_NS(EnumType) _enum::_data_ ## EnumType #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, ...) \ #define _ENUM_TYPE(EnumType, Integral, ...) \
class EnumType : public _ENUM_NS(EnumType)::_Base { \ class EnumType : public _ENUM_NS(EnumType)::_Base { \
protected: \ protected: \
@ -727,7 +716,10 @@ class EnumType : public _ENUM_NS(EnumType)::_Base { \
return _from_string_nocase_loop(name); \ 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 &_values = _ENUM_NS(EnumType)::_values; \
constexpr static auto &_names = _ENUM_NS(EnumType)::_names; \ constexpr static auto &_names = _ENUM_NS(EnumType)::_names; \

View File

@ -10,15 +10,15 @@ void respond_to_channel(Channel channel)
// Try adding an extra case or removing one. Your compiler should issue a // Try adding an extra case or removing one. Your compiler should issue a
// warning. // warning.
switch (channel) { switch (channel) {
case Channel::Red: case +Channel::Red:
std::cout << "red channel" << std::endl; std::cout << "red channel" << std::endl;
break; break;
case Channel::Green: case +Channel::Green:
std::cout << "green channel" << std::endl; std::cout << "green channel" << std::endl;
break; break;
case Channel::Blue: case +Channel::Blue:
std::cout << "blue channel" << std::endl; std::cout << "blue channel" << std::endl;
break; break;

View File

@ -10,7 +10,7 @@ constexpr Enum maximum(Enum accumulator = Enum::_values[0], size_t index = 1)
{ {
return return
index >= Enum::_size ? accumulator : index >= Enum::_size ? accumulator :
Enum::_values[index] > accumulator ? +Enum::_values[index] > accumulator ?
maximum(+Enum::_values[index], index + 1) : maximum(+Enum::_values[index], index + 1) :
maximum(accumulator, index + 1); maximum(accumulator, index + 1);
} }
@ -19,7 +19,7 @@ ENUM(Channel, int, Red, Green, Blue);
int main() int main()
{ {
using ChannelSet = std::bitset<maximum<Channel>() + 1>; using ChannelSet = std::bitset<maximum<Channel>().to_integral() + 1>;
ChannelSet red_only; ChannelSet red_only;
red_only.set(Channel::Red); red_only.set(Channel::Red);
@ -33,7 +33,7 @@ int main()
std::cout std::cout
<< channel.to_string() << channel.to_string()
<< " bit is set to " << " bit is set to "
<< red_and_blue[channel] << red_and_blue[channel.to_integral()]
<< std::endl; << std::endl;
} }

View File

@ -56,13 +56,21 @@ static_assert_1(!(std::is_constructible<Channel, Depth>()));
// Intended implicit conversions. // Intended implicit conversions.
static_assert_1((std::is_convertible<Channel::_Enumerated, Channel>())); static_assert_1((std::is_convertible<Channel::_Enumerated, Channel>()));
static_assert_1(!(std::is_convertible<Channel::_Integral, Channel>()));
static_assert_1(!(std::is_convertible<Depth, Channel>()));
static_assert_1(!(std::is_convertible<Channel, Depth>()));
// Regrettable implicit conversions. // Regrettable implicit conversions.
static_assert_1((std::is_convertible<Channel, Channel::_Enumerated>())); static_assert_1((std::is_convertible<decltype(Channel::Red),
static_assert_1((std::is_convertible<Channel, Channel::_Integral>())); Channel::_Integral>()));
// Disallowed implicit conversions.
static_assert_1(!(std::is_convertible<Channel::_Integral, Channel>()));
static_assert_1(!(std::is_convertible<Channel, Channel::_Integral>()));
static_assert_1(!(std::is_convertible<Depth, Channel>()));
static_assert_1(!(std::is_convertible<Channel, Depth>()));
static_assert_1(!(std::is_convertible<Channel, Channel::_Enumerated>()));
static_assert_1(!(std::is_convertible<decltype(+Channel::Red),
Channel::_Integral>()));
static_assert_1(!(std::is_convertible<decltype(Channel::_values[0]),
Channel::_Integral>()));