In my project, I'm using multiple enum classes which I need to easily cast between depending on where I need to use them. They basically describe the same thing but are named differently to keep the code more easier to work with. Here are the enum classes:
enum class tetroType {
None, I, O, T, J, L, S, Z
};
enum class gridBlock {
Empty, Blue, Green, Orange, Purple, Red, Teal, Yellow
};
Each value in tetroType corresponds to a value in gridBlock (f. e. tetroType::I = gridBlock::Teal), but the first holds the information about the shape of tetronimo (in tetronimo class) and the second holds information about color of blocks (in grid class). I know I could just use one enum instead but this way you don't loose any information. Also I'd need to cast it into string if possible. This is how I would like to use it:
gridBlock grid = (gridBlock)tetroType::I;
string texture = (string)grid;
Right now the way I've got it set up is like this. I'm using this switch in the middle of other methods whenever I need to convert one enum into another or into a string:
switch (type) {
case tetroType::I:
block = gridBlock::Teal;
break;
case tetroType::O:
block = gridBlock::Yellow;
break;
case tetroType::T:
block = gridBlock::Purple;
break;
case tetroType::J:
block = gridBlock::Blue;
break;
case tetroType::L:
block = gridBlock::Orange;
break;
case tetroType::S:
block = gridBlock::Green;
break;
case tetroType::Z:
block = gridBlock::Red;
break;
case tetroType::None:
block = gridBlock::Empty;
}
You should look into overloading the enum class as an integer (this is a C++ 11 feature).
enum class tetroType : int {
I = 1, O = 2, T = 3, J = 4, L = 5, S = 6, Z = 7, NONE
};
enum class gridBlock : int {
Blue = 1, Green = 2, Orange = 3, Purple = 4, Red = 5, Teal = 6, Yellow = 9, EMPTY
};
From here you can write a basic conversion using ether C Style typecasting, or static_cast
gridBlock ConvertTetro(tetroType type){
return static_cast<gridBlock>(static_cast<int>(type));
}
gridBlock ConvertTetro(tetroType type){
return (gridBlock)((int)((type)));
}
this will match any grid block to equal tetrotypes, and will default to gridBlock::EMPTY if theres no matching type. This function should be pretty easy to figure out how to go the other way if needed. From here you need to match up the int values between the two.
You can also use char values as char literals ('A', 'b', '!') using
enum class enumName : char
This will work as long as the two underlying types
Simple answer: Dont use enum class
, use normal enum
instead, they can be implicitly cast to their underlying type (default int
).
In C++ 11, enum class
is a strong alias which HAS to be casted from.
If you accept the fact that you must keep the definition of one of the two enum coherent to the other, so that each gridBlock
takes a value from tetroType
then you can override operator==
to use them seamlessly in comparisons and override a different operator (eg <<=
) to mimic assignment between different types.
Something like this:
#include <iostream>
#include <type_traits>
#include <cassert>
using namespace std;
enum class tetroType {
None, I, O, T, J, L, S, Z
};
enum class gridBlock {
Empty = static_cast<std::underlying_type<tetroType>::type>(tetroType::None),
Blue = static_cast<std::underlying_type<tetroType>::type>(tetroType::I),
Green = static_cast<std::underlying_type<tetroType>::type>(tetroType::O),
Orange = static_cast<std::underlying_type<tetroType>::type>(tetroType::T),
Purple = static_cast<std::underlying_type<tetroType>::type>(tetroType::J),
Red = static_cast<std::underlying_type<tetroType>::type>(tetroType::L),
Teal = static_cast<std::underlying_type<tetroType>::type>(tetroType::S),
Yellow = static_cast<std::underlying_type<tetroType>::type>(tetroType::Z)
};
bool operator==(const tetroType& t, const gridBlock& g) { return static_cast<gridBlock>(t) == g; }
bool operator==(const gridBlock& g, const tetroType& t) { return static_cast<gridBlock>(t) == g; }
bool operator!=(const tetroType& t, const gridBlock& g) { return static_cast<gridBlock>(t) != g; }
bool operator!=(const gridBlock& g, const tetroType& t) { return static_cast<gridBlock>(t) != g; }
gridBlock& operator<<=(gridBlock& g, tetroType t) { g = static_cast<gridBlock>(t); return g; }
tetroType& operator<<=(tetroType& t, gridBlock g) { t = static_cast<tetroType>(g); return t; }
int main() {
tetroType t1 = tetroType::I, t2 = tetroType::O;
gridBlock g1 = gridBlock::Blue, g2 = gridBlock::Green;
gridBlock g3;
g3 <<= t1;
tetroType t3;
t3 <<= g2;
assert(t1 == g1);
assert(t1 != g2);
assert(g3 == t1);
assert(t3 == g2);
return 0;
}
While this solution is concise, it's quite cryptic and obscure so you'd better document this behavior clearly. But in general with an enum class
is quite safe to override operators like operator<<=
since they are not defined in any case.
Mind that you can use a custom mapping between the values but if you don't need them, since you can initialize the values of one with the other, then there is no need to manually map them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With