Correct way to convert an enum to value - c++

I work on a project that has a lot of enums defined like this:
enum BundleSize
{
BUNDLE_SIZE_5 = 5,
BUNDLE_SIZE_10 = 10,
BUNDLE_SIZE_107 = 107,
};
I wrote a function to get the value from it like:
int convertBundleSizeEnumToVal(BundleSize b)
{
switch(b)
{
case BUNDLE_SIZE_5: return 5; break;
case BUNDLE_SIZE_10: return 10; break;
case BUNDLE_SIZE_107: return 107; break;
default: // handle appropriately and error out
}
}
I realize I don't really need a converter (method 1 and 2 both work below):
BundleSize b = getRandomBundleSize();
printf("The size is %d\n", convertBundleSizeEnumToVal(b)); // method 1
printf("The size is %d\n", b); // method 2
But I feel that method 1 is still the "correct" way to do this, because in the future something like this might come along:
enum BundleSize
{
BUNDLE_SIZE_5 = 5,
BUNDLE_SIZE_5_POINT_5 = 6,
BUNDLE_SIZE_10 = 10,
BUNDLE_SIZE_107 = 107,
};
Which now effectively breaks method 2 without an obvious runtime error, whereas method 1 will atleast catch the problem in the default switch case.
I'm curious about what people think is the right way to handle these conversions.

You can use the static_cast operator:
static_cast<int>(b)
IMO, this is "correct". You shouldn't be defining functions to convert every possible enum variant to an integer, especially with large enums.

Actually, you are using simple enum, so you can simply use your enum variable as a value.
For enum class (strict version of enum) you should use static_cast to convert your enum to value.

You can simplify your function to:
int convertBundleSizeEnumToVal(BundleSize b)
{
switch(b)
{
case BUNDLE_SIZE_5:
case BUNDLE_SIZE_10:
case BUNDLE_SIZE_107:
return b;
default: // handle appropriately and error out
}
}
Method 2, is broken because the underlying type of enum is not necessarily int, and so the format specifier %d may be wrong and behaviour of the program may be undefined.
You can cast the enum value to int first, and it will be correct as long as you don't use higher values than can be represented by int. You can use either static_cast or explicit cast. Or you can use an implicit conversion:
int value = getRandomBundleSize();
printf("The size is %d\n", b);
Regarding which you should use, well it depends on what you need. If you think that enum values added later should not be converted by convertBundleSizeEnumToVal but should result in an error, then method 1 is indeed superior.
If you wish all enum values to be converted, then the function is error prone, as you would have to remember to add a case each time a new value is added. In this case method 2 is superior.

Related

Casting invalid value to an enum via static_cast [duplicate]

This question already has an answer here:
What happens if you static_cast invalid value to enum class?
(1 answer)
Closed 7 months ago.
Doesn't static_cast protect against 'invalid' values to the type it's being cast to i.e Values here?
If so, it's not a good idea to print the casted value? How to ensure the print only takes place once the value is correct/valid?
enum class Values : uint8_t
{
One,
Two
};
void bar(uint8_t value)
{
Values val = static_cast<Values >(value);
printf ("The received value = %u\n", val);
switch(val)
{
case One: break;
case Two: break;
default: break;
}
}
int main()
{
bar(static_cast<uint8_t>(60));
}
Seemingly static_cast doesn't protect against 'invalid' values so in this snippet, how should the verification be done? Something like following or there's a better way?
enum class Values : uint8_t
{
One,
Two,
Max
};
void bar(uint8_t value)
{
Values val = static_cast<Values>(value);
if (val < Values::Max)
printf ("The received value = %u\n", val);
switch(val)
{
case One: break;
case Two: break;
default: printf ("invalid value!"); break;
}
}
No, there are no checks and there is nothing wrong with using a static_cast like this, no matter what the value is.
You specified that Values has underlying type uint8_t, so it is perfectly fine to (static_) cast between Values and uint8_t freely. There doesn't need to be a declared enumerator with a given value for the value to be valid for the enumeration. All values of the underlying type are also valid values of the enumeration type.
If you want to assure that the value is one of the enumerators, you need to write a function checking that yourself and decide on how it should act if the value does not satisfy the condition. (Should it throw an exception?)
(The rules are different if you don't specify an underlying type in an unscoped enumeration, in which case there is no fixed underlying type to substitute for uint8_t in the above and not all values of the implementation-defined underlying type are valid for the enumeration.)

How to use C++11 enum class for flags

Say I have such a class:
enum class Flags : char
{
FLAG_1 = 1;
FLAG_2 = 2;
FLAG_3 = 4;
FLAG_4 = 8;
};
Now can I have a variable that has type flags and assign a value 7 for example? Can I do this:
Flags f = Flags::FLAG_1 | Flags::FLAG_2 | Flags::FLAG_3;
or
Flags f = 7;
This question arises because in the enum I have not defined value for 7.
You need to write your own overloaded operator| (and presumably operator& etc.).
Flags operator|(Flags lhs, Flags rhs)
{
return static_cast<Flags>(static_cast<char>(lhs) | static_cast<char>(rhs));
}
Conversion of an integer to an enumeration type (scoped or not) is well-defined as long as the value is within the range of enumeration values (and UB otherwise; [expr.static.cast]/p10). For enums with fixed underlying types (this includes all scoped enums; [dcl.enum]/p5), the range of enumeration values is the same as the range of values of the underlying type ([dcl.enum]/p8). The rules are trickier if the underlying type is not fixed - so don't do it :)
It's maybe better to make use of std::underlying_type instead of hard-coding char type.
Flags operator|(Flags lhs, Flags rhs) {
return static_cast<Flags>(
static_cast<std::underlying_type<Flags>::type>(lhs) |
static_cast<std::underlying_type<Flags>::type>(rhs)
);
}
Now, you can change the underlying type of your enumeration without needing to update that type in every bitwise operator overload.
It should handle any enumeration type. I'm not sure it doesn't have any side effects and is completely valid C++ code. Let me know if there are some issues.
template<class T, std::enable_if_t<std::is_enum_v<T>, int> = 0>
constexpr T operator|(T lhs, T rhs)
{
return static_cast<T>(
static_cast<std::underlying_type<T>::type>(lhs) |
static_cast<std::underlying_type<T>::type>(rhs));
}
Please don't do this. If you need to do this, enum classs probably aren't what you need.
#T.C. showed you how to do it so long as you specify underlying type, but you will run into places where your program does things it just shouldn't.
An example is where you use a switch and have a case for every defined enum value.
e.g.
enum class my_enum: unsigned int{
first = 1,
second = 2,
third = 4,
fourth = 8
};
int main(){
auto e = static_cast<my_enum>(static_cast<unsigned int>(my_enum::first) | static_cast<unsigned int>(my_enum::second));
switch(e){
case my_enum::first:
case my_enum::second:
case my_enum::third:
case my_enum::fourth:
return 0;
}
std::cout << "Oh, no! You reached a part of the program you weren't meant to!\n";
return 1;
}
Will output:
Oh, no! You reached a part of the program you weren't meant to!
then return the error code 1.
Which is also an example of why you should always have a default case, of course, but that isn't my point.
Of course, you could argue that so long as the user of the enum class never directly uses the value other than passing to a function; it would be a good way of restricting the values of a bitset. But I've always been a little too trustworthy and find std::uint[n]_t and some constexpr variables the best way (if a user sets an invalid bit it simply does nothing).
What you're doing just isn't really suitable for enum class, because it defeats the purpose of having a scoped enumeration. You can no longer enumerate the values if you set it to an undefined one.
I realize this question is a bit old, but I will write out a method I have used to do this.
(If anything, if I Google this again in the future I have it documented to find again.)
Personally I like this method because intellisense (at least the VSCode version of it... I don't have Visual Studio on Linux...) will automatically pick up on what you're doing and give you useful hints. Also, it avoids the use of macros, so the compiler can warn you if it is not happy. Lastly, without the comments, it isn't a lot of code. You're not making a class and setting a bunch of overloads or anything, but you're still getting the benefit of scoped enums so that you can reuse a flag name/value for another enum. Anyway onto the example.
namespace FlagsNS
{
/* This is an old/classic style enum so put it in a
namespace so that the names don't clash
(ie: you can define another enum with the values of
Flag_1 or Flag_2, etc... without it blowing up)
*/
enum Flags
{
Flag_1 = 1 << 0, //Same as 1
Flag_2 = 1 << 1, //Same as 2
Flag_3 = 1 << 2, //Same as 4
Flag_4 = 1 << 3 //Same as 8
};
}
/* This is telling the compiler you want a new "type" called Flags
but it is actually FlagsNS::Flags. This is sort of like using the
#define macro, except it doesn't use the preprocessor so the
compiler can give you warnings and errors.
*/
using Flags = FlagsNS::Flags;
//Later in code.... so int main() for example
int main()
{
//If you don't mind c-style casting
Flags flag = (Flags)(Flags::FLAG_1 | Flags::FLAG_2 | Flags::FLAG_3);
//Or if you want to use c++ style casting
Flags flag = static_cast<Flags>(Flags::FLAG_1 | Flags::FLAG_2 | Flags::FLAG_3);
//Check to see if flag has the FLAG_1 flag set.
if (flag & Flags::FLAG_1)
{
//This code works
}
}
The code in question doesn't compile. But you can do something like this,
enum class Flags : char
{
FLAG_1 = 1,
FLAG_2 = 2,
FLAG_3 = 4,
FLAG_4 = 8,
};
int main() {
Flags f = static_cast<Flags>(7);
Flags f1 = static_cast<Flags>( static_cast<char>(Flags::FLAG_1) | static_cast<char>(Flags::FLAG_2) | static_cast<char>(Flags::FLAG_3) );
return 0;
}
and it works
At this point, It probably makes sense to define your own class to handle this.
/** Warning: Untested code **/
struct Flag {
static Flag Flag_1;
static Flag Flag_2;
static Flag Flag_3;
static Flag Flag_4;
Flag operator = (Flag);
private:
char const value;
};
Flag operator | (Flag, Flag);

Switching on scoped enum

I am trying to switch on a scoped-enum with the type unsigned int:
The enum is defined as:
const enum struct EnumType : unsigned int { SOME = 1, MORE = 6, HERE = 8 };
I receive a const unsigned int reference and I am trying to check that value against the enum values.
void func(const unsigned int & num)
{
switch (num)
{
case EnumType::SOME:
....
break;
case EnumType::MORE:
....
break;
....
default:
....
}
}
This results in a syntax error: Error: This constant expression has type "EnumType" instead of the required "unsigned int" type.
Now, using a static_cast on each switch, such as:
case static_cast<unsigned int>(EnumType::SOME):
....
break;
case static_cast<unsigned int>(EnumType::MORE):
....
break;
fixes the syntax error, although casting at each case statement doesn't seem like a good way to do this. Do I really need to cast at each case, or is there a better way?
You can solve this by casting the switch variable itself to EnumType:
switch (static_cast<EnumType>(num)) {
(Demo)
The purpose of scoped enums is to make them strongly-typed. To this end, there are no implicit conversions to or from the underlying type. You have to convert either the switch variable or the switch cases. I would suggest converting the switch variable since this requires less code and therefore will make maintenance easier.
IMO the correct solution would be to change the function to accept const EnumType & (or just EnumType) instead.

Why can't I increment a variable of an enumerated type?

I have a enumerated type StackID, and I am using the enumeration to refer to an index of a particular vector and it makes my code easier to read.
However, I now have the need to create a variable called nextAvail of type StackID. (it actually refers to a particular stackID ). I tried to increment it but in C++, the following is illegal:
nextAvail++;
Which sort of makes sense to me ... because there's no bounds checking.
I'm probably overlooking something obvious, but what's a good substitute?
I also want to link to this question.
I'm probably overlooking something obvious, but what's a good substitute?
Overloading operator++:
// Beware, brain-compiled code ahead!
StackID& operator++(StackID& stackID)
{
#if MY_ENUMS_ARE_CONTIGUOUS && I_DO_NOT_WORRY_ABOUT_OVERFLOW
return stackID = static_cast<StackID>( ++static_cast<int>(stackID) );
#else
switch(stackID) {
case value1 : return stackID = value2;
case value2 : return stackID = value3;
...
case valueN : return stackID = value1;
}
assert(false);
return stackID; // some compilers might warn otherwise
#endif
}
StackID operator++(StackID& stackID, int)
{
StackID tmp(stackID);
++stackID;
return tmp;
}
Because enumerations do not have to be contiguous. E.g. take this example:
enum Colors {
cRed, // = 0
cBlue, // = 1
cGreen = 3
}
What should happen in this scenario?
Colors color = cBlue;
Colors other = color++;
Should other be cGreen or should it be 2. In that case it's not a valid enumeration member anymore. What about this?
Colors color = cGreen;
Colors other = color++;
Should other be cRed (wrap around) or 4?
As you can see, being able to increment enumeration values introduces a whole lot of questions and complicates the simple mechanism that they intend to be.
If all you care about is the integer value being incremented, then simply cast to int and increment that.
Casting back and forth to/from int is of course the obvious solution, then you make clear that you understand that the addition is happening "outside" the enum:
nextAvail = static_cast<StackID>(static_cast<int>(nextAvail) + 1);
Why not store nextAvail as an int instead if you're going to do arithmetic operations on it?
Another option would be to wrap the enum in your own type and overload operator ++ for it (which also could wrap around or something for instance).
An enumeration is semantically supposed to represent a set of distinct related, values.
So you could have
enum Colour {RED, GREEN, BLUE};
But that should be equivalent to:
enum Colour {GREEN, BLUE, RED};
The problem is that if you increment an enum then those representations are not the same. GREEN++ in the first case is not the same as GREEN++ in the second.
Making your program dependent on the declaration of the enum is a recipe for disaster - maintainers may assume that the order of the enum doesnt matter, introducing many silent bugs.
Very Simple:
nextAvail = (StackID)(nextAvail + 1);
Enums are going to be type int, so you can cast them. Is this what you're trying to do?
int ndx = (int) StackID.SomeValue;
...
++ndx;
This is going to make someone very confused down the line, of course.
It occurs to me that you're using an enum where you should be using const, or even #define. enum is most appropriate when you have arbitrary values (where the exact value is not meaningful).
I've overloaded the ++/-- operator in this way:
enum STATE {STATE_1, STATE_2, STATE_3, STATE_4, STATE_5, STATE_6};
// Overload the STATE++ operator
inline STATE& operator++(STATE& state, int) {
const int i = static_cast<int>(state)+1;
state = static_cast<STATE>((i) % 6);
return state;
}
// Overload the STATE-- operator
inline STATE& operator--(STATE& type, int) {
const int i = static_cast<int>(type)-1;
if (i < 0) {
type = static_cast<STATE>(6);
} else {
type = static_cast<STATE>((i) % 6);
}
return type;
}
With respect to oprator++, $5.2.6/1 states- "The type of the operand shall be an arithmetic type or a pointer to a complete object type."
StackID does not fit the bill here. It is of enumeration type.
One option is like this
$5.7/1 - "For addition, either both operands shall have arithmetic or enumeration type, or one operand shall be a pointer to a completely defined object type and the other shall have integral or enumeration type."
enum Possibility {Yes, No, Maybe};
Possibility operator++(Possibility const& r){
return Possibility(r + 1); // convert r to integer, add 1, convert back to Enum
}
int main(){
Possibility p = Yes;
Possibility p1 = ++p;
}
I'm quite happy with this C plus C++ solution for a for loop incrementing an enum.
for (Dwg_Version_Type v = R_INVALID; v <= R_AFTER; v++)
=>
int vi;
for (Dwg_Version_Type v = R_INVALID;
v <= R_AFTER;
vi = (int)v, vi++, v = (Dwg_Version_Type)vi)
The other solutions here are not C backcompat, and quite large.

Enum C++ Get by Index

I was wondering in C++ if I have an enum can I access the value at the second index? For example I have
enum Test{hi, bye};
if I want 'hi', can I do something like Test[0], thanks.
Yes and no. If your Enum does not have explicit values then it is possible. Without an explicit values, enum values are given numeric values 0-N in order of declaration. For example ...
enum Test {
hi, // 0
bye // 1
}
This means that indexes just translates into a literal value.
Test EnumOfIndex(int i) { return static_cast<Test>(i); }
This of course does 0 validation at runtime and as soon as you add an explicit value it will break down. But it will work in the default scenario.
Unless specified otherwise, enums start numbering at 0, and increment by 1 each entry.
enum Test
{
hi, //0
bye, //1
count //2
}
You can cast an int to the type of the enum to get the value you want, such as:
(Test)0;
//or
Test(0);
Which lets you do things like:
for(int i = 0; i < count; i++)
{
DoSomething((Test)i);
}
Enumerations map names to values. In your case, (int)hi would have a value of 0, and (int)bye a value of 1. You can use a cast to get the value of hi:
int myInteger = 0;
Test myValue = (Test)myInteger;
Note, though, that myValue could be an invalid enum value if myInteger is out of range.
No, but you could cast from int
Test test = (Test)0;
Depends what you mean by "I want 'hi'".
If you mean you want the value, then you can get it by casting an int, as others have said.
Casting a numeric literal to enum type is usually pointless - if you know which value you're expecting, you can use the name. That way, the enum can change without breaking your code. I guess it's possible that something really weird is going on, where someone has created an enum, and documented what "3" means but not which enum value it is. But then you'd want to fix the API.
Casting an integer value known at runtime to enum might be helpful if you have serialized data. As long as you know it's in range of the enum, the result is defined.
If you mean you want the string "hi", then you can't have it. Unlike Java, in C++ the names of the values in enumerated types exist only at compile time, not at runtime, and only map in one direction.
Your best option might be something like this:
enum Test{hi = 0, bye};
Then you can simply refer to 'hi' with the number 0, and 'bye' with 1.
Although this really defeats the whole purpose of using an enumeration in the first place.
If you are excepting the value to returned as {Hi or bye} ,then you cannot get the value like that .
i would not suggest this to be done inorder to get the actual value but it can be used as hack
string return_value(int index)
{
string temp = "";
switch (index)
{
case 1: temp = "hi"
break;
case 2: temp = "bye";
break;
defualt :
break;
}
return temp;
}
typecasting to enum would again return the index but you can assign to some other enum variable
#include <iostream>
#define GENERATE_ENUM(ENUM) ENUM,
#define GENERATE_STRING(STRING) #STRING,
#define FOREACH_TEST(ID) ID(hi) ID(bye) ID(good)
enum TEST { FOREACH_TEST(GENERATE_ENUM) };
static const char * Test[] = { FOREACH_TEST(GENERATE_STRING) };
int main() {
printf("%s ",Test[0]);
printf("%s\n",Test[bye]);
for (int i=0; i<2; i++) printf("Test[%d] = %s\n", i, Test[i]); }
compile and run with: g++ enum-test.cpp -o enum-test; ./enum-test
output:
hi bye
Test[0] = hi
Test[1] = bye