I need to use scoped enums so that I can pass them as specific types to our serialiser. I have given explicit integer values for the enum members of Enum1.
I have put two scoped enums matching the description above into a bitfield thus
enum class Enum1 {
value1 = 0x0,
value2 = 0x1,
value3 = 0x2
};
enum class Enum2 {
value1 = 0x0,
value2,
value3,
// ...
value14
};
struct Example {
Enum1 value1 : 2;
Enum2 value2 : 6;
}
Now wherever I use the Example type, I get the warning "'Example::value1' is too small to hold all values of 'Enum1'", and similarly for Enum2. Note that this is not the case for the values we have defined and we are not concerned at all with values outside of these.
This is quite a serious distraction in our build process - the project is large and complex and we don't want to have to scan through many of these warnings (and there are many).
I had a look for a GCC (G++) flag to disable the specific warning. Is there one that I can pass on the command line? Ideally, I would use the warning pragma to disable it locally, if possible.
There is little scope for changing the code structure at this point, but we could really use these spurious warnings removed.
Edit: Added scoped enums with identifiers changed.
The problem is that an scoped enum always has an integral underlying type. By default, it is int but you can change it to any other integral type, such as unsigned char.
Unfortunately you cannot change the underlying type to a bit-field, as they are not real C++ types.
You could try disabling the warning, but a quick skim through the G++ code reveals these lines (gcc/cp/class.c:3468):
else if (TREE_CODE (type) == ENUMERAL_TYPE
&& (0 > (compare_tree_int
(w, TYPE_PRECISION (ENUM_UNDERLYING_TYPE (type))))))
warning_at (DECL_SOURCE_LOCATION (field), 0,
"%qD is too small to hold all values of %q#T",
field, type);
The key here is the call to warning_at(...) instead of warning(OPT_to_disable_the_warning, ...). So currently there is no option to disable it. Except recompiling the compiler yourself!
For what it is worth CLang++-3.7.1 does not warn about it.
As I recall, an enum with a declared underlying type can hold any value of that type, regardless of what enumeration constants are defined. Since you can say
val= enum2{148}
and expect it to work properly, the warning seems correct for that case. You are not declaring a base type, and historically this means that the enum is only guaranteed to be big enough to hold the range of values given by the lowest through highest enumeration constant. So I would expect no warning here. Maybe the new enum class also expects a full range even though the underlying type was automatically determined (or the compiler thinks it does)? You might try using a pure old-syntax enum and see if that works any differently.
Emitting this warning is a bug, because all declared enumerator (values) in fact can be held by the bitfield fields.
Like with traditional enums, a variable of scoped enum type still can hold any value of its underlying type, even ones that don't correspond to a declared enumerator.
However, warning about this like this
warning: 'some bitfield field' is too small to hold all values of 'enum class FOO'
is quite pointless because assigning a too large value as in
Example x;
x.value1 = Enum1(8);
already generates a -Woverflow warning.
Consequently, GCC fixed this warning in version 9.3.
FWIW, Clang never warned about.
IOW, to suppress this warning in GCC you have to upgrade to GCC version 9.3 or later.
For other people like me who end up here from search:
This problem only applies to C++11 scoped enums. If you need bitfield enums, old style enums without explicit storage size work fine:
enum Enum1 {
Enum1_value1 = 0x0,
Enum1_value2 = 0x1,
Enum1_value3 = 0x2
};
enum Enum2 {
Enum2_value1 = 0x0,
Enum2_value2,
Enum2_value3,
// ...
Enum2_value14
};
struct Example {
Enum1 value1 : 2;
Enum2 value2 : 6;
}
Related
Suppose I have an enum:
enum class Thing : std::uint8_t {
Foo = 21,
Bar = 42,
Baz = 1
};
I want to convert a "raw" value (of the underlying type) to a value of that enum, with error handling catching values which are not "a thing". I could use checks like if (raw_value == static_cast<std::uint8_t>(Thing::Foo)) and then an else with the error handling, but then I could forget for example Thing::Baz. However, when I switch over a value of type Thing, then my compiler (as probably most modern compilers) warns me about unhandled enumeration values ("enumeration value Baz not handled in switch").
So I came up with this:
Thing thingFromUInt8(std::uint8_t const b) {
Thing const t = static_cast<Thing>(b);
switch (t) { // If I'd add Thing::Frob but forget it here I get a warning
case Thing::Foo:
case Thing::Bar:
case Thing::Baz:
return t;
}
throw std::runtime_error("That's not a thing...");
}
Questions:
Is this "legal" C++ (C++11 and above)?
If it is, does it have any drawbacks?
It is legal C++.
The drawback is the DRY violation, but avoiding it is difficult.
In c++23 we'll have reflection and be able to generate equivalent code (without having to rely on compiler warnings to make sure we didn't miss any). Reflection syntax is still a bit in flux, but every version I have read over was able to handle that problem.
I have recently noticed a fact that was to me quite surprising:
you apparently can retrieve any arbitrary value from an enum type and assign it to an enum variable even if it is not part of the enum definition (what I call a "ghost value"). The variable simply takes the corresponding integer value.
Example:
#include <iostream>
enum myEnumType { ONE = 1, TWO = 2, THREE = 3 };
int main () {
myEnumType e;
e=myEnumType(8);
std::cout << e << std::endl;
}
which outputs 8.
I would have thought this wasn't possible, as it seems to me at odds with the constraining function of enumerated types.
What is the reason and, most importantly, the possible utility of this?
You were allowed to cast an int to an enum. C++11 introduced "enumeration classes" or "strong enums" or "scoped enums" to deal with this among other problems.
Other benefits include their scope and allowing forward declaration.
There are lots of details on Stroustrup's page and he specifically mentions three problems with the traditional enums:
conventional enums implicitly convert to int, causing errors when someone does not want an enumeration to act as an integer.
conventional enums export their enumerators to the surrounding scope, causing name clashes.
the underlying type of an enum cannot be specified, causing confusion, compatibility problems, and makes forward declaration impossible.
You are calling to constructor of the enum passing 8, so the value assigned to that instance is 8.
It happens the same thing when you call to enum default constructor, it sets 0 as default value.
myEnumType e = myEnumType();
std::cout << e << std::endl; // prints 0
It prints 0 (although the enum is only initialized with ONE = 1, TWO = 2, THREE = 3), here it is better explained: http://lifecs.likai.org/2010/07/c-enum-default-constructor.html
I just read some code similar to next one:
enum
{
width = 123,
height = 321,
position_x = 234
position_y = 432
};
...
Widget* w = CreateWidget(position_x, position_y, width, height);
Is there any reason to use enum in this case instead of macros or const values?
EDIT: Is it correct to use enum like this? Is this usage considered some kind of abuse in enum usage?
There are plenty of reasons not to use macros. The enum in the question is scoped and won't interfere with the same identifier used in different scopes, so you can for example, defined a member position_x in a class without the macro mangling your class definition.
Comparing the enum to a constant, there are people that prefer the enum as it is guaranteed that it will not add to the binary size of the executable. In the case of a constant, it may add (a bit, well, actually an int) to the size of the binary.
No, there's no special reason to choose an enum over macros or const int values in this case.
Editorial note: It's certainly legal code to use enum in this fashion, but it is a bit strange looking at first glance.
In this particular case, there doesn't appear to be any real deciding factor when choosing between an enum and constant values. Both are better than a macro however, and macros should be avoided in general.
More generally, there are some differentiating aspects between an enum and constants:
enums are distinct types, which is more expressive than integral values
you can take the address of a constant, but you can't take the address of an enum value
It may be more convenient to use enum than static const int as a class member.
class A
{
static const int Foo = 42;
// may have to define A::Foo somewhere
};
class B
{
enum { Foo = 42 };
// done
};
The typical way to define an integer constant to use inside a function is:
const int NumbeOfElements = 10;
the same for using within a class:
class Class {
...
static const int NumberOfElements = 10;
};
It can then be used as a fixed-size array bound which means it is known at compile time.
Long ago compilers didn't support the latter syntax and that's why enums were used:
enum NumberOfElementsEnum { NumberOfElements = 10; }
Now with almost every widely used compiler supporting both the in-function const int and the in-class static const int syntax is there any reason to use the enum for this purpose?
The reason is mainly brevity. First of all, an enum can be anonymous:
class foo {
enum { bar = 1 };
};
This effectively introduces bar as an integral constant. Note that the above is shorter than static const int.
Also, no-one could possibly write &bar if it's an enum member. If you do this:
class foo {
static const int bar = 1;
}
and then the client of your class does this:
printf("%p", &foo::bar);
then he will get a compile-time linker error that foo::bar is not defined (because, well, as an lvalue, it's not). In practice, with the Standard as it currently stands, anywhere bar is used where an integral constant expression is not required (i.e. where it is merely allowed), it requires an out-of-class definition of foo::bar. The places where such an expression is required are: enum initializers, case labels, array size in types (excepting new[]), and template arguments of integral types. Thus, using bar anywhere else technically requires a definition. See C++ Core Language Active Issue 712 for more info - there are no proposed resolutions as of yet.
In practice, most compilers these days are more lenient about this, and will let you get away with most "common sense" uses of static const int variables without requiring a definition. However, the corner cases may differ, however, so many consider it to be better to just use anonymous enum, for which everything is crystal clear, and there's no ambiguity at all.
Defining static constants directly in the class definition is a later addition to C++ and many still stick to the older workaround of using an enum for that. There might even be a few older compilers still in use which don't support static constants directly defined in class definitions.
Use of enum have one advantage. An enum type is a type, so if you define, for example:
enum EnumType { EnumValue1 = 10, EnumValue2 = 20 ... };
and you have a function like:
void Function1(EnumType Value)
the compiler checks that you are passing a member of the enum EnumType to the function, so only valid values for parameter Value would be EnumValue1 and EnumValue2. If you use constants and change the function to
void Function1(int Value)
the compiler checks that you are passing an int (any int, a constant, variable or literal) to the function.
Enum types are good for grouping related const-values. For only one const value, I do not see any advantage.
In your case, I'd use a constant as well. However, there are other cases where I might be adding other, related constants. Like this:
const int TextFile = 1; // XXX Maybe add other constants for binary files etc.?
In such cases, I use an enum right away with a single value, like this:
enum FileType {
TextFile = 1
// XXX Maybe add other values for binary files etc.?
}
The reason is that the compiler can then issue warnings when I'm using the constant value in switch expressions, as in:
FileType type;
// ...
switch ( type ) {
case TextFile:
// ...
}
In case I decide to add another constant value which is related to the existing value (a different type of file, in this example), virtually all compilers will issue a warning since the new value is not handled in the switch statement.
If I had used 'int' and constants instead, the compiler wouldn't have a chance to issue warnings.
The only reason for using the "enum hack" is that old compilers do not support in-class const definitions, as you say in your question. So, unless you suspect that your code will be ported to an old compiler, you should use const where const is due.
I think that there's no reason to use an enum, and that it's actually better to use a static const int for this purpose, since an enum has its own type (even if implicitly convertible to an integer).
There is a difference between those two. Enums don't have an adress as far as I know. static const ints do though. So if someone takes the adress of the const static int, casts away the const, he can modify the value (although the compiler might ignore the change because he thinks it's const). This is of course pure evil and you should not do it - but the compiler can't prevent it. This can't happen with enums.
And of course - if you (for some reason) need the adress of that const, you need the static const int.
In short - enum is an rvalue while const static int is an lvalue. See http://www.embedded.com/story/OEG20011129S0065 for more details.
Well, the portability is a good reason for using enum. It is great, because you don't have to worry whether your compiler supports "static const int S = 10" or not...
Also, as far as I remember, static variable must be defined somewhere, as well as declared, and the enum value must be declared only.
bottom line - use const.
more details:
I'm not a c++ expert, but this seems a more general design question.
If it's not a must, and you think there's a very low/non-existent probability that the enum will grow to have more then one value, then use a regular const.
even if you are wrong and at some point in the future there will be more values, making an enum the right choice - a simple refactoring and you change you const to enum.
We have a template conversion function intended for use with numeric datatypes. Inside it contains a construct that makes it not compile with types like pointers.
template<class To, class From>
To ConvertTo( From what )
{
assert( 2 * what == what * 2 ); // this will not compile for pointers
//skipped
}
This function compiles and works allright when a enum is passed as the second template parameter:
enum TSomeEnum {
SE_First,
SE_Second
};
TSomeEnum enumValue = SE_First;
int result = ConvertTo<int>( enumValue );
The above code compiles and runs as intended on VC++7.
How does the operation * work for enums? Is it a way to undefined behaviour?
Enums degrade to ints (old C feature) so that why this is working. I don't think it's undefined behaviour although it might be the sort of behaviour you don't want/expect.
Enums are promoted to integers for arithmetic operations.
The values of enums are always numbered up from zero (this is part of the standard), but you can specify whatever values you like. Automatic numbering then procedes incrementally from the last item you explicitly numbered.
The compiler uses the smallest integral type that contains all of the values of an enum to store it.
eg:
enum foo
{
VALUE0, /* = 0 */
VALUE1, /* = 1 */
VALUE3 = 1234,
VALUE4 /* = 1235 */
};
I think peterchen has the ideal answer, but if boost is too much for you, change your current version to:
template<class To, class From>
To ConvertTo( From what, From checkConvertableToInt = 2 )
{
}
C++ doesn't allow implicit conversions from integers to enumerations. Similarly, it's only possible to implicitly convert the null pointer constant (0) to a pointer type.
Some of the other answers refer to an enumeration being implemented as an int or being promoted to an int. This is not true. In C++, an enumeration is defined to have an underlying type that is capable of storing all values in the enumeration. The following enumeration will therefore have an underlying type of 'unsigned int' or a larger signed/unsigned integral type:
enum E {
E0
, E1=MAX_INT+1u
};
The enumeration then follows the rules for its underlying type when you use it in an operation.
Timo described why it works.
For a safer test, you could use boost::TypeTraits
Richard Corden's answer gives a good mechanism to force enums to fail in the assert, however the side effect can cause problems, as you cannot be sure the code inside the assert will always run.
This test should probably by placed in a generic function to avoid the side effects.
template<class To, class From>
To ConvertTo( From what )
{
assert( testIntegralType(what) );
}
template<class T>
void testIntegralType( T val )
{
T v1 = val;
assert( 2*v1 == 2*val );
}
enums are stored as integers, and can even be cast to integers.
The compiler probably stored your above enum as
enum TSomeEnum {
SE_First = 0,
SE_SEcond = 1
};
I'm not sure if this numbering system is part of the C/C++ standard or just how compilers do it, but I've always seen them in order.
Anyway, to answer your question, since they're stored as ints underneath, it should be able to be multiplied. Some compilers may give you a warning about type conversion though.