This question already has an answer here:
My research has not yielded an answer for this yet (in SOF), but I am sure it must have been asked somewhere before, appologies if so.
I have created an enumerated type in c++ and then I read in a value from a message header and want to store it into a variable of my enum type, example:
// My defined enum type
enum myNumType_t
{
MY_NUM_UNKNOWN = 0,
MY_NUM1 = 1,
MY_NUM2 = 2,
MY_NUM3 = 3,
MY_NUM4 = 4,
MY_NUM5 = 5,
};
// In the code
int main()
{
myNum_t myNum = MY_NUM_UNKNOWN;
myNum = getMessageNumType(); // returns an int
return 0;
}
So, this code does not compile in c++ because it can't convert the int to myNum_t, fair enough. So then if I cast it myNum = (myNum_t) getMessageNumType();
this of course now compiles. But does it do the right thing? What happens if the returned value is out of range of myNum_t? Is there a "best practise" here that I am missing?
Yes, it is safe to cast from int type to enumeration type.
§ 4.5 Integral promotions
Just return enumeration type so you could avoid any cast troubles:
The only really safe int-to-enum cast is to cover ALL cases in the enum and in error-checking that the input might return and include boundary enums if there is a possibility of going out of range or returning an invalid value and then handle the bad value gracefully.
If the enum is guaranteed to be a contiguous range then it is a little simpler:
Assuming the value is valid for the enumeration type, yes.
It depends.
Up to (and excluding) the nearest power of two, you're guaranteed to get exactly that value. What your code then does with it is up to you. In other words, given your code,
(int)(myNumType_t)7
is guaranteed to be equal to 7. This is important because some people use bitwise operations on enumeration values, so if an enumeration hasBIT0 = 1, BIT1 = 2, BIT2 = 4
, it is intended thatBIT0 | BIT1 | BIT2
can be stored in that same enumeration type.For larger values, not much of use can be said. Some implementations would store your enumeration in a single (8-bit) byte, so on those implementations,
(int)(myNumType_t)257
could not possibly be equal to 257. On others, it might be. You're better off avoiding that by checking the integer value beforehand.You can use a cast to convert an integer value to an enumeration. If you do that, you're responsible for ensuring that the resulting value is meaningful. In the example code, you should make sure that
getMessageNumType()
returns a suitable value.Values stored in an enumerated type are not restricted to the values named by the enumerators. For example, it's quite common to use an enumerator, along with appropriate overloaded operators, to implement a bit mask:
That
|
operator should really be done with an overloaded operator; I left that out for simplicity. The cast is okay, but gets tedious to write everywhere.The first question is whether you can trust the source of the value or not, and in general if you are reading messages off the network or any other input/output device, I would doubt that trust.
If you cannot trust it, you should be able to write a small function that tests the value received and maps it to the appropriate enumerator or fails if the value is out of range. Note that in the example code you provided, the compiler could choose any type for the enumeration that was able to represent all the values in the range 0-7, the standard does not make any additional guarantees.
While in practical terms the compiler will most probably pick a larger type like
int
for this particular case, the compiler can assume that the value is not outside of that range. If the value decoded from the message is larger than 8 you will be hitting undefined behavior and you might get results you don't really expect.For a practical example, if the enumerators ranged from 0 to 3, both included, and you had this code:
The compiler is allowed to remove the
default
case, as all values that can be represented in theenum_t
are explicitly enumerated in theswitch
, so thedefault:
case cannot be hit in a well formed program. The optimizer can change that test into:And you might end up being surprised as of why all values are valid even when your test case sends messages with invalid values!