I've come to a pattern when writing enums in C++. It is like this:
class Player
{
public:
class State
{
public:
typedef enum
{
Stopped,
Playing,
Paused
}PossibleValues;
static const int Count() {return Paused+1;};
static const PossibleValues Default() {return Stopped;};
};
//...
}
This solves a some of the usual issues with enums, like pollution of outside namespaces, etc. But there is still a thing I don't like: The Count() is done manually. There are only two ways I know how to do it: this one is calculated from Last+1; or write plain hardcoded.
Question is: Is there some way, like using preprocessor macros, that automatically gets the count, to put it after in the Count() method? Attention: I don't want to have a last fake element called Count inside the enum, polluting it!
Thanks in advance!
UPDATE 1:
There is an interesting discussion on Implementation of N4428 enum reflection in standard C++11 (partial) for a proposal of more advanced enums.
UPDATE 2:
Interesting document N4451- Static reflection (rev. 3) on its sections 3.16, 3.17, A.7, A.8 about MetaEnums and MetaEnumClasses.
UPDATE 3:
I came to another interesting pattern using an enum class, after I've seen https://bytes.com/topic/c/answers/127908-numeric_limits-specialization#post444962. If the enum class's enumerator list is continuously integer, by defining its maximum and its minimum, we can check whether a value belongs to it or not.
If the purpose of using the Count()
method on the Player::State
was to check if a value was in the enum, that purpose has also been achieved with the numeric_limits approach, and is even superior, as it is not required the enumerator list begins with a ZERO valued item!
enum class Drink
{
Water,
Beer,
Wine,
Juice,
};
#pragma push_macro("min")
#undef min
#pragma push_macro("max")
#undef max
namespace std
{
template <> class numeric_limits < Drink >
{
public:
static const/*expr*/ bool is_specialized = true;
static const/*expr*/ Drink min() /*noexcept*/ { return Drink::Water; }
static const/*expr*/ Drink max() /*noexcept*/ { return Drink::Juice; }
static const/*expr*/ Drink lowest() /*noexcept*/ { return Drink::Water; }
static const/*expr*/ Drink default() /*noexcept*/ { return Drink::Beer; }
};
}
#pragma pop_macro("min")
#pragma pop_macro("max")
CASES OF USE:
A variable from the application:
Drink m_drink;
which in constructor is initialized with:
m_drink = numeric_limits<Drink>::default();
On the initialization of a form, I can do:
pComboDrink->SetCurSel(static_cast<int>(theApp.m_drink));
On it, for adapting the interface to changes done by the user, I can do a switch with scoped enum class values:
switch (static_cast<Drink>(pComboDrink->GetCurSel()))
{
case Drink::Water:
case Drink::Juice:
pAlcohoolDegreesControl->Hide();
break;
case Drink::Beer:
case Drink::Wine:
pAlcohoolDegreesControl->Show();
break;
default:
break;
}
And on the dialog's confirmation procedure (OnOK
), I can check if the value is out of boundaries, before saving it to the respective app var:
int ix= pComboDrink->GetCurSel();
if (ix == -1)
return FALSE;
#pragma push_macro("min")
#undef min
#pragma push_macro("max")
#undef max
if (ix < static_cast<int> (std::numeric_limits<Drink>::min()) || ix > static_cast<int> (std::numeric_limits<Drink>::max()) )
return FALSE;
#pragma pop_macro("min")
#pragma pop_macro("max")
theApp.m_drink= static_cast<Drink>(ix);
NOTES:
- The keywords
constexpr
(I commented/*expr*/
, leaving it asconst
) andnoexcept
are commented only because the compiler I am using (Visual C++ 2013) does not support them yet at the current version. - Maybe you do not need the logic to temporary undefine the min and max macros.
- I know that the
default()
does not fit on a "numeric limits" scope; but it seemed an handy place to put it on; even it coincides with thedefault
word that in some contexts is a keyword!