I'm trying to understand a C++ code given here. Apologies if the question is too basic, but I am not a regular C++ coder.
Here are two Enums defined in the code and a a few functions that operate on the Enums:
...
enum Value {
VALUE_ZERO = 0,
VALUE_DRAW = 0,
VALUE_KNOWN_WIN = 10000,
VALUE_MATE = 32000,
VALUE_INFINITE = 32001,
VALUE_NONE = 32002,
...
};
...
/// Score enum stores a middlegame and an endgame value in a single integer.
/// The least significant 16 bits are used to storethe endgame value and
/// the upper 16 bits are used to store the middlegame value. ...
enum Score {
SCORE_ZERO,
SCORE_ENSURE_INTEGER_SIZE_P = INT_MAX,
SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN
};
inline Score make_score(int mg, int eg) {
return Score((mg << 16) + eg)
}
/// Extracting the signed lower and upper 16 bits is not so trivial ...
inline Value mg_value(Score s) {
union {uint16_t u; int16_t s;} mg = {uint16_t(unsigned(s + 0x8000) >> 16)};
return Value(mg.s);
}
inline Value eg_value(Score s) {
union {uint16_t u; int16_t s;} eg = {uint16_t(unsigned(s))};
return Value(eg.s);
}
Questions:
(a) From my basic understanding of Enum in C++, SCORE_ZERO in the enum Score gets a value of 0, and thus the set of values of enum Score is 0, MAX_INT, or MIN_INT. The function make_score seems to accepts any two int (mg, eg) and returns an instance of Score. How does it (or the compiler) decide whether to return Score with value assigned to one out of 0, MAX_INT, or MIN_INT?
(b) Similar, the Enum Value only has a fixed set of values. How does Value(mg.s) in the function mg_value, or Value(eg.s) in the functioneg_valuedecide which values fromValue` to pick?
(c) In general, if XYZ is an Enum and a function returns XYZ(n) where n is not a value already defined in Enum, how is the Enum initialized?
How does it (or the compiler) decide whether to return Score with value assigned to one out of 0, MAX_INT, or MIN_INT?
It doesn't. When you force a cast to Score using Score((mg << 16) + eg), you can use any value, not necessarily one of the above declared constants.
It is valid to use:
Score s1 = (Score)10;
Score s2 = Score(20);
How does Value(mg.s) in the function mg_value, or Value(eg.s) in the function eg_value decide which values from Value to pick?
It doesn't. The answer to the previous question regarding Score applies here as well.
In general, if XYZ is an enum and a function returns XYZ(n) where n is not a value already defined in the enum, how is the enum initialized?
The answer to the first question applies here as well. The compiler doesn't guarantee that the value of a variable of type XYZ will be one of the constants defined in the enum.
Related
Is there a way to check in C++11 if an enum is continuous?
It is fully valid to give an enum values which are not. Is there maybe a feature like a type trait in C++14, C++17 or maybe C++20 to check is the enum is continuous? This to be used in a static_assert.
A small example follows:
enum class Types_Discontinuous {
A = 10,
B = 1,
C = 100
};
enum class Types_Continuous {
A = 0,
B = 1,
C = 2
};
static_assert(SOME_TEST<Types_Discontinuous>::value, "Enum should be continuous"); // Fails
static_assert(SOME_TEST<Types_Continuous>::value, "Enum should be continuous"); // Passes
This is not possible in pure C++, because there is no way to enumerate the enum values, or discover the number of the values and minimum and maximum values. But you could try using the help of your compiler to implement something close to what you want. For example, in gcc it is possible to enforce a compilation error if a switch statement does not handle all values of an enum:
enum class my_enum {
A = 0,
B = 1,
C = 2
};
#pragma GCC diagnostic push
#if __GNUC__ < 5
#pragma GCC diagnostic error "-Wswitch"
#else
#pragma GCC diagnostic error "-Wswitch-enum"
#endif
constexpr bool is_my_enum_continuous(my_enum t = my_enum())
{
// Check that we know all enum values. Effectively works as a static assert.
switch (t)
{
// Intentionally no default case.
// The compiler will give an error if not all enum values are listed below.
case my_enum::A:
case my_enum::B:
case my_enum::C:
break;
}
// Check that the enum is continuous
auto [min, max] = std::minmax({my_enum::A, my_enum::B, my_enum::C});
return static_cast< int >(min) == 0 && static_cast< int >(max) == 2;
}
#pragma GCC diagnostic pop
Obviously, this is specialized for a given enum, but definition of such functions can be automated with preprocessor.
For a number of enums you can probably hack your way through this using the Magic Enum library. For example:
#include "magic_enum.hpp"
template <typename Enum>
constexpr bool is_continuous(Enum = Enum{}) {
// make sure we're actually testing an enum
if constexpr (!std::is_enum_v<Enum>)
return false;
else {
// get a sorted list of values in the enum
const auto values = magic_enum::enum_values<Enum>();
if (std::size(values) == 0)
return true;
// for every value, either it's the same as the last one or it's one larger
auto prev = values[0];
for (auto x : values) {
auto next = static_cast<Enum>(magic_enum::enum_integer(prev) + 1);
if (x != prev && x != next)
return false;
else
prev = x;
}
return true;
}
}
Note that this is indeed, as the library name implies, "magic" – the library functions on a number of compiler-specific hacks. As such it doesn't really meet your requirement of "pure C++", but is probably as good as we can get until we have reflection facilities in the language.
I'd love to see an answer on this. I've been needing it as well.
Unfortunately, I don't think this is possible using the existing utilities. If you want to implement a type trait on this, you need support from your compiler, so writing a template for it doesn't sound feasible.
I've already extended the enumeration with a specific tag to indicate it is contiguous and immediately gives you the size: enum class constructor c++ , how to pass specific value?
Alternatively, you can write your own trait:
template<T> struct IsContiguous : std::false_type {};
This needs to be specialized whenever you define an contiguous enum where you want to use this. Unfortunately, this requires some maintenance and attention if the enum gets changed.
All enum's are continuous. 0 is always allowed; the highest value allowed is the highest enumerator rounded up to the next 1<<N -1 (all bits one), and all values in between are allowed too. ([dcl.enum] 9.7.1/5). If there are negative enumerators defined, the lowest value allowed is similarly defined by rounding down the lowest enumerator.
The enumerators defined in the enum are constant expressions with a value in range and the correct type, but you can define additional constants outside the enum which have the same properties:
constexpr enum class Types_Discontinuous = static_cast<Types_Discontinuous>(2)
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.
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();
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.
Consider the following (simplified) code:
enum eTestMode
{
TM_BASIC = 1, // 1 << 0
TM_ADV_1 = 1 << 1,
TM_ADV_2 = 1 << 2
};
...
int m_iTestMode; // a "bit field"
bool isSet( eTestMode tsm )
{
return ( (m_iTestMode & tsm) == tsm );
}
void setTestMode( eTestMode tsm )
{
m_iTestMode |= tsm;
}
Is this reliable, safe and/or good practice? Or is there a better way of achieving what i want to do apart from using const ints instead of enum? I would really prefer enums, but code reliability is more important than readability.
I can't see anything bad in that design.
However, keep in mind that enum types can hold unspecified values. Depending on who uses your functions, you might want to check first that the value of tsm is a valid enumeration value.
Since enums are integer values, one could do something like:
eTestMode tsm = static_cast<eTestMode>(17); // We consider here that 17 is not a valid value for your enumeration.
However, doing this is ugly and you might just consider that doing so results in undefined behavior.
There is no problem. You can even use an variable of eTestMode (and defines bit manipulation for that type) as it is guaranteed to hold all possible values in that case.
See also
What is the size of an enum in C?
For some compilers (e.g. VC++) this non-standard width specifier can be used:
enum eTestMode : unsigned __int32
{
TM_BASIC = 1, // 1 << 0
TM_ADV_1 = 1 << 1,
TM_ADV_2 = 1 << 2
};
Using enums for representing bit patterns, masks and flags is not always a good idea because enums generally promote to signed integer type, while for bit-based operation unsigned types are almost always preferable.