I have an enum type in my code, like this:
enum class GameConsts: size_t { NUM_GHOSTS = 4 };
I find myself repeating the required static_cast
to get the enum
value:
Ghost ghosts[static_cast<size_t>(GameConsts::NUM_GHOSTS)];
// and...
for(size_t i = 0; i < static_cast<size_t>(GameConsts::NUM_GHOSTS); i) { ... }
What is the best way to avoid this repetitive static_cast
?
The option of allowing implementation of a casting operator was raised in this ISO proposal discussion, but seems to drop.
Related SO question: Overloading cast operator for enum class
Davis Herring added in a comment that C 23 added: std::to_underlying which should be the answer when we have C 23 compiler support. But the question is for C 20.
CodePudding user response:
Your first option is to use a constexpr
instead of an enum:
constexpr size_t NUM_GHOSTS = 4;
You can put it inside a proper context, such as GameConsts struct:
struct GameConsts {
static constexpr size_t NUM_GHOSTS = 4;
};
Then there is no need for a casting:
Ghost ghosts[GameConsts::NUM_GHOSTS];
In case you actually need an enum, as you have a list of related values that you want to manage together, then you may use unscoped enum (i.e. drop the class
from the enum class
and use a regular plain-old enum
) but put it inside a struct to preserve the context. I will use here an example of another enum, managing several related values.
enum class GameKeys: char { UP = 'W', RIGHT = 'D', DOWN = 'X', LEFT = 'A' };
The repeated use of static_cast
may happen for the above enum, in a switch-case like that:
char key_pressed;
// ...
switch(key_pressed) {
case static_cast<char>(GameKeys::UP): // ...
break;
case static_cast<char>(GameKeys::RIGHT): // ...
break;
case static_cast<char>(GameKeys::DOWN): // ...
break;
case static_cast<char>(GameKeys::LEFT): // ...
break;
}
To avoid the repeated need for static_cast
you may go with:
Option 1: Use simple unscoped enum inside a struct
struct GameKeys {
enum: char { UP = 'W', RIGHT = 'D', DOWN = 'X', LEFT = 'A' };
};
And since old-style enum can cast implicitly to its underlying type, you can get away of the casting:
switch(key_pressed) {
case GameKeys::UP: // ...
break;
case GameKeys::RIGHT: // ...
break;
case GameKeys::DOWN: // ...
break;
case GameKeys::LEFT: // ...
break;
}
Option 2: Add your own conversion function
If you actually prefer, or have to use enum class
you may have a simple conversion function for which the copy-paste is just calling the function, being less cumbersome than the full static_cast
syntax:
enum class GameKeys: char { UP = 'W', RIGHT = 'D', DOWN = 'X', LEFT = 'A' };
// a simple "val" function - specific for our GameKeys
constexpr char val(GameKeys key) { return static_cast<char>(key); }
And:
switch(key_pressed) {
case val(GameKeys::UP): // ...
break;
case val(GameKeys::RIGHT): // ...
break;
case val(GameKeys::DOWN): // ...
break;
case val(GameKeys::LEFT): // ...
break;
}
If you choose the last option, you may want to generalize it for any type of enum
, with this code:
// creating a "concept" for enums
template<typename E>
concept EnumType = std::is_enum_v<E>;
// creating a generic "val" function for getting the underlying_type value of an enum
template<EnumType T>
constexpr auto val(T value) {
return static_cast<std::underlying_type_t<T>>(value);
}
Option 3: Cast to the enum and not from the enum
As suggested by @Nathan Pierson and @apple apple in the comments, the casting can be to the enum, with this code:
char key_pressed = 'E';
// cast to the enum
GameKeys key = static_cast<GameKeys>(key_pressed);
switch(key) {
case GameKeys::UP: // ...
break;
case GameKeys::RIGHT: // ...
break;
case GameKeys::DOWN: // ...
break;
case GameKeys::LEFT: // ...
break;
default: // ignore any other keys
break;
}
This should work fine even if key_pressed
is not any of the enum values, as we have a fixed enum (having a stated underlying type, note that an enum class is always fixed, even if not stated explicitly). See also: What happens if you static_cast invalid value to enum class?
CodePudding user response:
If you are constantly converting a scoped enum to an integer... you don't actually want a scoped enum. What you seem to want is a bunch of static constexpr
variables in their own namespace:
struct GameConsts
{
static constexpr size_t NumGhosts = 4;
...
};