How to assign subsequent bits to C enumeration members?

Viewed 105

Here's what I got:

enum X {
    NONE = 0x00000000,
    FLAG_1 = 0x00000001,
    FLAG_2 = 0x00000002,
    FLAG_3 = 0x00000004,
    FLAG_4 = 0x00000008,
    FLAG_5 = 0x00000010,
    // ...
    FLAG_32 = 0x80000000
}

Is there a way to make "bit numbering" automatic so I could like insert a flag so all that goes next get "renumbered"?

I'm just designing an API and I want to keep related flags together, ordered in a specific sequence. The problem is when I add something that goes in the middle I have to manually reassign all numbering that goes after the inserted item. Let's say in my example I want to add FLAG_2A = 0x00000004, and FLAG_3 should be 0x00000008 and so on. Is there a "full auto" way of doing it?

OK, here's the first thing that comes to mind:

#include <stdio.h>

enum { __FLAGS1_BASE = __COUNTER__ };
#define __FLAGS1_CT 1 << (__COUNTER__ - __FLAGS1_BASE - 1)

typedef enum __TEST1 {
    FLAG1_0 = 0,
    FLAG1_1 = __FLAGS1_CT,
    FLAG1_2 = __FLAGS1_CT,
    FLAG1_3 = __FLAGS1_CT,
    FLAG1_4 = __FLAGS1_CT
} TEST1;

enum { __FLAGS2_BASE = __COUNTER__ };
#define __FLAGS2_CT 1 << (__COUNTER__ - __FLAGS2_BASE - 1)

typedef enum __TEST2 {
    FLAG2_0 = 0,
    FLAG2_1 = __FLAGS2_CT,
    FLAG2_2 = __FLAGS2_CT,
    FLAG2_3 = __FLAGS2_CT,
    FLAG2_4 = __FLAGS2_CT
} TEST2;

int main() {
    printf("X = %u\n", FLAG2_3); // should output 4.
    return 0;
}

Is it the only way, or is there something simpler than that?

6 Answers

I threw together a quick implementation of the MAKE_FLAGS macro HolyBlackCat suggested:

#define E3(...) E2(E2(E2(E2(E2(E2(E2(__VA_ARGS__)))))))
#define E2(...) E1(E1(E1(E1(E1(E1(E1(E1(__VA_ARGS__))))))))
#define E1(...) __VA_ARGS__

#define EMPTY()
#define TUPLE_AT_2(x,y,...) y

#define CHECK(...) TUPLE_AT_2(__VA_ARGS__,0,)
#define EQ_END_END ,1

#define CAT(a,b) CAT_(a,b)
#define CAT_(a,b) a##b

#define LOOP_() LOOP
#define LOOP(x,y,...) CAT(LOOP, CHECK(EQ_END_##y))(x,y,__VA_ARGS__)
#define LOOP1(x,...) 
#define LOOP0(x,y,...) y = x << 1, LOOP_ EMPTY() ()(y,__VA_ARGS__)

#define HEAD(x,...) x
#define MAKE_FLAGS(name,...) typedef enum { HEAD(__VA_ARGS__,) = 1, E3(LOOP(__VA_ARGS__, END)) } name 

MAKE_FLAGS(MyEnum, flag1, flag2, flag3, flag4);
// expands to:
// typedef enum { flag1 = 1, flag2 = flag1 << 1, flag3 = flag2 << 1, flag4 = flag3 << 1, } MyEnum;

Here is an alternative approach:

enum X_bits {
    B0,  // replace Bx with actual flag name
    B1,
    B2,
    //...
    B32
};

#define FLAG(x)  FLAG_##x = 1U << x
enum X {
    NONE = 0,
    FLAG(B0),  // will define FLAG_B0 with the appropriate value 0x1
    FLAG(B1),
    FLAG(B2),
    //...
    FLAG(B32)
};

Actual bit numbers and bit marks are computed automatically.

Same idea as @chqrlie's answer (using a second enum to generate sequental indices), but generated with a macro:

#define MAKE_FLAGS(name_, zero_, seq_) \
    enum CAT(BitIndices_, name_) { END( IMPL_MAKE_FLAGS_loop1_a seq_ ) }; \
    typedef enum name_ { zero_ = 0, END( IMPL_MAKE_FLAGS_loop2_a seq_ ) } name_;

#define CAT(x, y) CAT_(x, y)
#define CAT_(x, y) x##y

#define END(...) END_(__VA_ARGS__)
#define END_(...) __VA_ARGS__##_end

#define IMPL_MAKE_FLAGS_loop1_a(name_) CAT(bitindex_, name_), IMPL_MAKE_FLAGS_loop1_b
#define IMPL_MAKE_FLAGS_loop1_b(name_) CAT(bitindex_, name_), IMPL_MAKE_FLAGS_loop1_a
#define IMPL_MAKE_FLAGS_loop1_a_end
#define IMPL_MAKE_FLAGS_loop1_b_end

#define IMPL_MAKE_FLAGS_loop2_a(name_) name_ = 1ull << CAT(bitindex_, name_), IMPL_MAKE_FLAGS_loop2_b
#define IMPL_MAKE_FLAGS_loop2_b(name_) name_ = 1ull << CAT(bitindex_, name_), IMPL_MAKE_FLAGS_loop2_a
#define IMPL_MAKE_FLAGS_loop2_a_end
#define IMPL_MAKE_FLAGS_loop2_b_end

Then MAKE_FLAGS( E, none, (x)(y)(z) ) expands to:

enum BitIndices_E { bitindex_x, bitindex_y, bitindex_z, };
typedef enum E
{
    none = 0,
    x = 1ull << bitindex_x,
    y = 1ull << bitindex_y,
    z = 1ull << bitindex_z,
} E;

The best solution is to type it out manually, it's just 32 rows.

In case you insist on generating it with macros, then the least bad version is not to run amok with some "clever" contrived ones, but to go with a de facto standard solution using "x-macros":

#define FLAG_LIST(X) \
  X(1) X(2) X(3) X(4)  /* ... */

#define flag_decl(val) FLAG_##val = val,
#define flag_mask_decl(val) FLAG_MASK_##val = 1u << (val-1),

typedef enum 
{
  FLAG_0 = 0,
  FLAG_LIST(flag_decl)
} flag;

typedef enum 
{
  FLAG_MASK_0 = 0,
  FLAG_LIST(flag_mask_decl)
} flag_mask;

Pre-processor output of the above:

typedef enum
{
  FLAG_0 = 0,
  FLAG_1 = 1, FLAG_2 = 2, FLAG_3 = 3, FLAG_4 = 4,
} flag;

typedef enum
{
  FLAG_MASK_0 = 0,
  FLAG_MASK_1 = 1u << (1 -1), FLAG_MASK_2 = 1u << (2 -1), FLAG_MASK_3 = 1u << (3 -1), FLAG_MASK_4 = 1u << (4 -1),
} flag_mask;

My last take: flags.h header:

#pragma once

#define BIT_FLAG_BASE(base) enum { base = __COUNTER__ + 1 } ///< Sets the base for the bit flag counter.
#define BIT_FLAG(base) 1U << (__COUNTER__ - base) ///< Gets the next bit for the bit flag counter.

Used like this:

#include "flags.h"

BIT_FLAG_BASE(MyFlag);

enum MyFlags {
    B1 = BIT_FLAG(MyFlag),
    B2 = BIT_FLAG(MyFlag),
    // ...
    B31 = BIT_FLAG(MyFlag)
};

How many combinations are there? I am a verbose guy, I would do:

// for i in $(seq 3); do echo "#define BITFLAG_$i(N$(printf ",_%s" $(seq $i))) \\"$'\n'"enum N { \\$(seq $((i-1)) | xargs -i printf "\n\t_{} = (1 << {}), \\\\")"$'\n'"};"; done
#define BITFLAG_1(N,_1) \
enum N { \
};
#define BITFLAG_2(N,_1,_2) \
enum N { \
        _1 = (1 << 1), \
};
#define BITFLAG_3(N,_1,_2,_3) \
enum N { \
        _1 = (1 << 1), \
        _2 = (1 << 2), \
};
/* etc. */
    
#define BITFLAG_N(_10,_9,_8,_7,_6,_5,_4,_3,_2,_1,_0,N,...)  BITFLAG_##N
#define BITFLAG(...)  BITFLAG_N(__VA_ARGS__,10,9,8,7,6,5,4,3,2,1)(__VA_ARGS__)

BITFLAG(
    X,
    FLAG1,
    FLAG2,
)
Related