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 ar
Converting enum-to-int, e.g. int(VALUE_A), happens automatically/transparently.
Converting int-to-enum, e.g. En(ValA), can benefit from sanity checking to make sure the int value is a valid member of the enum. (Though hopefully the library code doesn't assume its enum values are valid in the first place.)
While it won't help with "int x" cases, you can help somewhat by changing:
#define ValA 5
To:
#define ValA VALUE_A
Provided enum En() is included/defined everywhere, both ValA and VALUE_A will work for both foo(int) and bar(En) everywhere automatically/transparently.
You could use:
#ifdef ValA
STATIC_ASSERT( ValA == VALUE_A, ValA_equal_VALUE_A );
#undef ValA
#else
#warning "ValA undefined. Defining as VALUE_A"
#endif
#define ValA VALUE_A
Where STATIC_ASSERT is something like:
/* Use CONCATENATE_4_AGAIN to expand the arguments to CONCATENATE_4 */
#define CONCATENATE_4( a,b,c,d) CONCATENATE_4_AGAIN(a,b,c,d)
#define CONCATENATE_4_AGAIN(a,b,c,d) a ## b ## c ## d
/* Creates a typedef that's legal/illegal depending on EXPRESSION. *
* Note that IDENTIFIER_TEXT is limited to "[a-zA-Z0-9_]*". *
* (This may be replaced by static_assert() in future revisions of C++.) */
#define STATIC_ASSERT( EXPRESSION, IDENTIFIER_TEXT) \
typedef char CONCATENATE_4( static_assert____, IDENTIFIER_TEXT, \
____failed_at_line____, __LINE__ ) \
[ (EXPRESSION) ? 1 : -1 ]
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.
Have functions and then overload the library functions?
//libFunc( enum a );
libFuncOverload( define a ) {
libFunc( toEn( a ) );
}
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).
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.