In the external code that I am using there is enum:
enum En {VALUE_A, VALUE_B, VALUE_C};
In another external code that I am using there are 3 #define directives:
#define ValA 5
#define ValB 6
#define ValC 7
Many times I have int X which is equal to ValA or ValB or ValC, and I have to cast it to the corresponding value of En (ValA to VALUE_A, ValB to VALUEB, etc) because some function signature has enum En. And many times I have to do the opposite operation, translate enum En to ValA or ValB or ValC. I cannot change the signatures of these functions, and there are many such functions.
The question is: How to do the translation? Should I create 2 cast operators, which will be used implicitly? Or should I just have 2 translation functions which will be used explicitly:
En ToEn(int)
int FromEn(En)
Or any other solution?
Since you can't just cast here, I would use a free function, and if there are likely to be other enums that also need converting, try to make it look a little bit like the builtin casts:
template<typename T>
T my_enum_convert(int);
template<>
En my_enum_convert<En>(int in) {
switch(in) {
case ValA: return VALUE_A;
case ValB: return VALUE_B;
case ValC: return VALUE_C;
default: throw std::logic_error(__FILE__ ": enum En out of range");
}
}
int my_enum_convert(En in) {
switch(in) {
case VALUE_A: return ValA;
case VALUE_B: return ValB;
case VALUE_C: return ValC;
// no default, so that GCC will warn us if we've forgotten a case
}
}
En enumValue = my_enum_convert<En>(ValA);
int hashDefineValue = my_enum_convert(VALUE_A);
enumValue = my_enum_convert<En>(0); // throws exception
Or something like that - might adjust it if issues arise while using it.
The reason I wouldn't use implicit conversion is that there already is an implicit conversion from En to int, which gives the wrong answer. Even if you can reliably replace that with something which gives the right answer, the resulting code won't look as though it's doing any conversion. IMO this will hinder anyone who later looks at the code more than typing a call to a conversion routine will hinder you.
If you want converting to int and converting from int to look very different, then you could give the template and the function above different names.
Alternatively, if you want them to look the same (and more like a static_cast), you could do:
template<typename T>
T my_enum_convert(En in) {
switch(in) {
case VALUE_A: return ValA;
case VALUE_B: return ValB;
case VALUE_C: return ValC;
}
}
int hashDefineValue = my_enum_convert<int>(VALUE_A);
As written, T must have an implicit conversion from int. If you want to support T that only has an explicit conversion, use "return T(ValA);" instead (or "return static_cast<T>(ValA);", if you consider that single-arg constructors are C-style casts and hence impermissible).
While implicit casting is more convenient than translation functions it is also less obvious to see what's going on. An approach being both, convenient and obvious, might be to use your own class with overloaded cast operators. When casting a custom type into an enum or int it won't be easy to overlook some custom casting.
If creating a class for this is not an option for whatever reason, I would go for the translation functions as readability during maintenance is more important than convenience when writing the code.
You can not overload operators for enums. Or am I missing something? Well, you could create some sort of dummy classes, that would have an implicit constructor taking an int, and then have a cast operator to an enum (and vice versa).
So, the only solution is to have functions. Also, I would make the overloads as Patrick suggests.
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