Home > Software design >  How to assign subsequent bits to C enumeration members?
How to assign subsequent bits to C enumeration members?

Time:06-13

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?

CodePudding user response:

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;

CodePudding user response:

Here is an alternative approach:

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

#define FLAG(x)  FLAG##x = 1U << x
enum X {
    NONE = 0,
    FLAG(B0),
    FLAG(B1),
    FLAG(B2),
    //...
    FLAG(B32)
};

Actual bit numbers and values are computed automatically.

CodePudding user response:

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;

CodePudding user response:

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;

CodePudding user response:

My other take to that is this:

#define __BASE(base) enum { base = __COUNTER__   1 }
#define __FLAG(base) 1 << (__COUNTER__ - base)

Can be used like this:

__BASE(__E);
enum E {
    B1 = __FLAG(__E),
    B2 = __FLAG(__E),
    B3 = __FLAG(__E),
    B4 = __FLAG(__E)
}

IDK, it just looks nice to me ;)

__BASE() and __FLAG() macros can be hidden in a common include to make code even cleaner.

  • Related