Is it safe to use an enum in a bit field?
Solution 1
Answer will be different for C and C++, this is one for C.
In C bitfields are restricted to signed int
, unsigned int
, _Bool
and int
which in this context can be any of the first two. Compiler implementors can add to that list to their liking but are required to document the types that they support.
So to answer your question, if you want to be absolutely sure that your code is portable to all C compilers, no, using an enum
type is not an option.
The corresponding paragraph from the current standard reads:
A bit-field shall have a type that is a qualified or unqualified version of _Bool, signed int, unsigned int, or some other implementation-defined type. It is implementation-defined whether atomic types are permitted.
Solution 2
It's allowed in all C++ compilers, that supports standard.
C++03 standard 9.6/3
A bit-field shall have integral or enumeration type (3.9.1). It is implementation-defined whether a plain (neither explicitly signed nor unsigned) char, short, int or long bit-field is signed or unsigned.
C++03 standard 9.6/4
If the value of an enu- merator is stored into a bit-field of the same enumeration type and the number of bits in the bit-field is large enough to hold all the values of that enumeration type, the original enumerator value and the value of the bit-field shall compare equal.
example
enum BOOL { f=0, t=1 };
struct A {
BOOL b:1;
};
void f() {
A a;
a.b = t;
a.b == t // shall yield true
}
But you can't consider that enum has unsigned underlying type.
C++03 standard 7.2/5
The underlying type of an enumeration is an integral type that can represent all the enumerator values defined in the enumeration. It is implementation-defined which integral type is used as the underlying type for an enumeration except that the underlying type shall not be larger than int unless the value of an enu- merator cannot fit in an int or unsigned int
Solution 3
No.
Bit fields are implemented significantly differently between compilers. If you define a bit-field with two values, zero and one, and try to have an enum typed bit field then you may hit these problems:
The bit field will be unsigned with gcc and clang, but signed with VC++. This means that in order to store zero and one you need a two-bit bit field (a one-bit signed bit field can only store zero and negative one).
Then you have to worry about packing. VC++ will only pack adjacent bit fields into the same backing store if their sizes match. I'm not sure what the rules are for gcc and clang, but for VC++ the default backing store for a bit field is an int. So, a series of bit fields that are, for instance, a mixture of bool and enum will pack extremely poorly with VC++.
You could try to solve this with C++ 11 typed enums:
enum Foo : unsigned char { one, two };
but then gcc complains if you use this in a one-bit bit field:
warning: ‘bitfieldTest::g’ is too small to hold all values of ‘enum Foo’ [enabled by default]
It seems there is no winning.
Comments
-
eckes over 3 years
Say, I've got the following struct:
typedef struct my_struct{ unsigned long a; unsigned long b; char* c; unsigned int d1 :1; unsigned int d2 :4; unsigned int d3 :4; unsigned int d4 :23; } my_type, *p_type;
The field
d3
is currently defined by#define
s that reach from0x00
until0x0D
.Actually,
d3
is an enumeration. So it's tempting to go ahead and replaceunsigned int d3 :4;
by
my_enum d3 :4;
Is this safe/allowed?
The code has to compile with various
- compilers (GCC, Visual Studio, embedded stuff)
- platforms (Win32, Linux, embedded stuff)
- configurations (compile as C, compile as C++)
Obviously, I could leave the definition of
d3
as it is and use the enum in my code, assign it tod3
and so on but that's not going to work with C++.