Confusing enum init - c++

What is the meaning of this code:
using namespace std;
enum DRAM_Controller { dram_controller_maximum = 10};
void myprint(DRAM_Controller dct)
{
printf("dct value is: %d\n", dct);
}
int main ()
{
DRAM_Controller(0); //**--> What is the meaing of this**
printf("value is : %d\n", dram_controller_maximum);
printf("value is : %d\n", DRAM_Controller(1));
myprint(DRAM_Controller(0));
}
The output is:
value is : 10
value is : 1
dct value is: 0

It's a cast from int to DRAM_Controller
It creates a temporary of type DRAM_Controller and has no effect

Here you assign a dram_controller_maximum to 10, which simply means that each time you write
something = dram_controller_maximum, you mean something = 10:
enum DRAM_Controller { dram_controller_maximum = 10};
For the following function, if you pass it a number, it will just print it. If you pass a DRAM_Controller variable, it will evaluate it's value (a number, remember), and print it.
void myprint(DRAM_Controller dct)
{
printf("dct value is: %d\n", dct);
}
The following line just transforms the integer (0) to DRAM_Controller. This line alone is pretty useless:
DRAM_Controller(0); //**--> What is the meaing of this**
The next three lines will print the dram_controller_maximum value converted to int. Remember, in the beginning we said it's equal to 10, so this will just print 10. All the three lines do the same thing: they try to interpret the DRAM_Controller-type value as an int and print it:
printf("value is : %d\n", dram_controller_maximum);
printf("value is : %d\n", DRAM_Controller(1));
myprint(DRAM_Controller(0));
Basically, an enum is just a bunch of ints that have "names":
C exposes the integer representation of enumeration values directly to
the programmer. Integers and enum values can be mixed freely, and all
arithmetic operations on enum values are permitted. It is even
possible for an enum variable to hold an integer that does not
represent any of the enumeration values. In fact, according to the
language definition, the above code will define CLUBS, DIAMONDS,
HEARTS, and SPADES as constants of type int, which will only be
converted (silently) to enum cardsuit if they are stored in a variable
of that type
and
C++ has enumeration types that are directly inherited from C's and
work mostly like these, except that an enumeration is a real type in
C++, giving additional compile-time checking.
from wiki.

The line you're pointing out,
DRAM_Controller(0);
casts a 0 to DRAM_Controller, then ignores it.
It has the same effect as
0;
and is just as useful.
In C++, any integer that can fit in an enum's size can be cast into that enum.
This is one of the reasons that a default: case is a good idea when you "switch-case" on an enum value.

It creates a temporary, unnamed object of type DRAM_Controller initialised with the given value.
The first line has no effect, since the object is destroyed without using its value. The last two lines use the object to initialise the function arguments.

Related

Why index casting into an enum is allowed but erroneous

I have learnt that enums are data types as opposed to data structures. I have also learnt that usually they are nominal in nature rather than ordinal. Consequentially, it does not make sense to iterate through an enum, or obtain the enum's constant value like you would an array, e.g. week[3].
However, I have come across instances where an index is used to obtain the value from an enum:
#include <iostream>
enum week {Mon=5, Tues, Wed};
int main()
{
week day = (week)0;
std::cout << day << "\n"; // outputs 0
day = (week)13;
std::cout << day << "\n"; // outputs 13
return 0;
}
How is this working?
I assumed (week)13 would not work, given there is nothing at this index. Is it that the cast is failing and falling back to the type to be cast?
As a side note: I believe some confusion with this style of code in C/C++ may occur due to other languages (C#) handling this case differently.
Edit:
The linked solutions don't answer the question I'm asking.
1 is about comparing integers to enums -- in this case I'm asking why casting an int to an enum gives a certain result.[2] (Assigning an int value to enum and vice versa in C++) mentions "Old enums (unscoped enums) can be converted to integer but the other way is not possible (implicitly). ", but by the accounts of my code, it does seem possible. Lastly 3 has nothing to do with this question: I'm not talking about assignment of enums, rather the casting of them via an integer.
The rule, roughly, is that any value hat fits in the bits needed to represent all of the enumerators can be held in an enumerated type. In the example in the question, Wed has the value 7, which requires three bits. So any value from 0 to 7 can be stored in an object of type week. Even if Wed wasn’t there, the remaining two values would need the same three low bits, so the values 0 to 7 would all still work. 13, on the other hand requires the fourth bit, which isn’t needed for any of the enumerators, so is not required to work.

How significant is (int) within a statement?

If I have:
#include <iostream>
int main()
{
float a,b,c;
b = 7;
c = 2;
a = (int) (b / c);
std::cout << a;
}
Does (int) only affect the data type during cout so that 'a' can be printed as an integer or does it affect 'a' as a variable changing it to an 'int'?
Does (int) only affect the data type during cout so that a can be printed as an integer or does it affect a as a variable changing it to an int?
Neither.
a = (int)(....);
only changes what is assigned to a. In this case it truncates the floating point number and assigns the integral part of the number to a.
It does not change how a is processed in cout << a. You will notice a truncated value in the output. However, the reason for that is that a truncated value got assigned to a in the previous statement not because cout << a is processed differently.
It does not change the type of a to an int. The type of a variable cannot be changed in C++. It remains unchanged for the entire life of the program.
In this particular case it converts from a float value, the result of b/c into an int, then as a is still a float, converts it back to a float.
This is an easy, if sometimes problematic way of rounding something to an integer value.
Remember that in C++ variables never change their fundamental type. Something defined as a float stays a float forever. You can force other values into the same memory location, you can recast them, but the variable itself always has a fixed intrinsic type.
Cast does not change the type of a variable the casted value is assigned to.
In your case, result of b/c is casted (truncated) to an int, which is then promoted to float.
In this case the int is a cast datatype.
What the computer are thinking
Inside the main function:
float a, b, c;
Declaring 3 variables of data_Type float.
b = 7;
c = 5;
Assigned value of 7 to b and value 5 to c.
a = (int) (b / c);
A is equal to b/c ==> 7/5 ==> 1.4, wait, the programmer asked to cast the data as int so 1.4 ==> 1
std::cout << a;
Output: 1.0
Hope this help

Comparing enums to integers

I've read that you shouldn't trust on the underlying implementation of an enum on being either signed or unsigned. From this I have concluded that you should always cast the enum value to the type that it's being compared against. Like this:
enum MyEnum { MY_ENUM_VALUE = 0 };
int i = 1;
if (i > static_cast<int>(MY_ENUM_VALUE))
{
// do stuff
}
unsigned int u = 2;
if (u > static_cast<unsigned int>(MY_ENUM_VALUE))
{
// do more stuff
}
Is this the best practice?
Edit: Does the situation change if the enum is anonymous?
An enum is an integer so you can compare it against any other integer, and even floats. The compiler will automatically convert both integers to the largest, or the enum to a double before the compare.
Now, if your enumeration is not supposed to represent a number per se, you may want to consider creating a class instead:
enum class some_name { MY_ENUM_VALUE, ... };
int i;
if(i == static_cast<int>(some_name::MY_ENUM_VALUE))
{
...
}
In that case you need a cast because an enum class is not viewed as an integer by default. This helps quite a bit to avoid bugs in case you were to misuse an enum value...
Update: also, you can now specify the type of integer of an enum. This was available in older compilers too, but it was often not working quite right (in my own experience).
enum class some_name : uint8_t { ... };
That means the enumeration uses uint8_t to store those values. Practical if you are using enumeration values in a structure used to send data over a network or save in a binary file where you need to know the exact size of the data.
When not specified, the type defaults to int.
As brought up by others, if the point of using enum is just to declare numbers, then using constexpr is probably better.
constexpr int MY_CONSTANT_VALUE = 0;
This has the same effect, only the type of MY_CONSTANT_VALUE is now an int. You could go a little further and use typedef as in:
typedef int my_type_t;
constexpr my_type_t MY_CONSTANT_VALUE = 0;
I often use enum even if I'm to use a single value when the value is not generally considered an integer. There is no set in stone rule in this case.
Short answer: Yes
enum is signed int type, but they get implicitly cast into unsigned int. Your compiler might give a warning without explicit casting, but its still very commonly used. however you should explicitly cast to make it clear to maintainers.
And of course, explicit cast will be must when its a strongly typed enum.
Best practice is not to write
int i = 1;
if (i > static_cast<int>(MY_ENUM_VALUE))
{
// do stuff
}
instead write
MyEnumValue i = MY_ENUM_VALUE ;
...
if ( i > MY_ENUM_VALUE ) {..}
But if - as in your example - you only have one value in your enum it is better to declare it as a constant instead of an enum.

is it correct to cast an integer to enumerated type? [duplicate]

This question already has an answer here:
What happens if you static_cast invalid value to enum class?
(1 answer)
Closed 9 years ago.
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?
But does it do the right thing?
Assuming the value is valid for the enumeration type, yes.
What happens if the returned value is out of range of myNum_t?
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 has BIT0 = 1, BIT1 = 2, BIT2 = 4, it is intended that BIT0 | 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.
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:
enum_t value = (enum_t) getInt();
switch (value) {
case V0:
case V1:
case V2:
case V3: std::cout << "valid\n"; break;
default: std::cout << "unknown value\n"; break;
}
The compiler is allowed to remove the default case, as all values that can be represented in the enum_t are explicitly enumerated in the switch, so the default: case cannot be hit in a well formed program. The optimizer can change that test into:
enum_t value = (enum_t) getInt();
std::cout << "valid\n";
And you might end up being surprised as of why all values are valid even when your test case sends messages with invalid values!
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:
enum myNumType_t
{
MY_NUM_UNKNOWN = 0,
MY_NUM_MIN = 1,
MY_NUM1 = 1,
MY_NUM2 = 2,
MY_NUM3 = 3,
MY_NUM4 = 4,
MY_NUM5 = 5,
MY_NUM_MAX = MY_NUM5,
};
int main() {
//...getMessage...
myNumType_t result = static_cast<myNumType_t>(getMessageNumType());
if(result < MY_NUM_MIN || result > MY_NUM_MAX) {
//..handle out of range...
}
}
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:
enum flag_values {
flag1 = 0x01,
flag2 = 0x02,
flag3 = 0x04,
flag4 = 0x08
};
flag_values flags = static_cast<flag_values>(flag1 | flag2);
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.
Yes, it is safe to cast from int type to enumeration type.
§ 4.5 Integral promotions
A prvalue of an unscoped enumeration type whose underlying type is fixed (7.2) can be converted to a prvalue of its underlying type. Moreover, if integral promotion can be applied to its underlying type, a prvalue of an unscoped enumeration type whose underlying type is fixed can also be converted to a prvalue of the promoted underlying type.
Just return enumeration type so you could avoid any cast troubles:
myNum_t getMessageNumType();

C++ union to represent data memory vs C scalar variable type

Today I've a weird question.
The Code(C++)
#include <iostream>
union name
{
int num;
float num2;
}oblong;
int main(void)
{
oblong.num2 = 27.881;
std::cout << oblong.num << std::endl;
return 0;
}
The Code(C)
#include <stdio.h>
int main(void)
{
float num = 27.881;
printf("%d\n" , num);
return 0;
}
The Question
As we know, C++ unions can hold more than one type of data element but only one type at a time. So basically the name oblong will only reserve one portion of memory which is 32-bit (because the biggest type in the union is 32-bit, int and float) and this portion could either hold a integer or float.
So I just assign a value of 27.881 into oblong.num2 (as you can see on the above code). But out of curiosity, I access the memory using oblong.num which is pointing to the same memory location.
As expected, it gave me a value which is not 27 because the way float and integer represented inside a memory is different, that's why when I use oblong.num to access the memory portion it'll treat that portion of memory value as integer and interpret it using integer representation way.
I know this phenomena also will happen in C , that's why I initialize a float type variable with a value and later on read it using the %d.So I just try it out by using the same value 27.881 which you can see above. But when I run it, something weird happens, that is the value of the one I get in C is different from C++.
Why does this happen? From what I know the two values I get from the two codes in the end are not garbage values, but why do I get different values? I also use the sizeof to verified both C and C++ integer and float size and both are 32-bit. So memory size isn't the one that causes this to happen, so what prompt this difference in values?
First of all, having the wrong printf() format string is undefined behavior. Now that said, here is what is actually happening in your case:
In vararg functions such as printf(), integers smaller than int are promoted to int and floats smaller than double are promoted to double.
The result is that your 27.881 is being converted to an 8-byte double as it is passed into printf(). Therefore, the binary representation is no longer the same as a float.
Format string %d expects a 4-byte integer. So in effect, you will be printing the lower 4-bytes of the double-precision representation of 27.881. (assuming little-endian)
*Actually (assuming strict-FP), you are seeing the bottom 4-bytes of 27.881 after it is cast to float, and then promoted to double.
In both cases you are encountering undefined behaviour. Your implementation just happens to do something strange.