Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

enum-int casting: operator or function

Tags:

c++

enums

casting

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?

like image 293
Igor Avatar asked Dec 15 '08 09:12

Igor


3 Answers

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).

like image 73
Steve Jessop Avatar answered Sep 22 '22 19:09

Steve Jessop


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.

like image 33
foraidt Avatar answered Sep 23 '22 19:09

foraidt


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.

like image 45
Paulius Avatar answered Sep 21 '22 19:09

Paulius