Is it safe to use an enum in a bit field?

21,292

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.

Share:
21,292
eckes
Author by

eckes

Nothing special here.

Updated on September 17, 2020

Comments

  • eckes
    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 #defines that reach from 0x00 until 0x0D.

    Actually, d3 is an enumeration. So it's tempting to go ahead and replace

        unsigned 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 to d3 and so on but that's not going to work with C++.