-2

In C its common to do...

enum {
    A,
    B,
    C,
};

However with flags you need to define them explicitly...

enum {
    A = (1 << 0),
    B = (1 << 1),
    C = (1 << 2),
};

Is it possible to define flags without having to explicitly list each value?

Since posting this question, there seems to be some confusion as to why you would even want to do this. To be clear, defining flags explicitly is typically a "good thing", so my question is about some fairly obscure corner case when you might want to avoid it.


Note: This assumes the flags are just 'slots' that there they aren't ever written to disk or used in a persistent way, which would break API's, network protocols. etc.

The ability to do this can rule out a small class of bugs where...

  • enum flags values must use consecutive bits.
  • enum flags bits must never overlap.

With benefit that adding/removing bits, doesn't require changing every value after that enum identifier.

Admittedly this isn't a feature you would want to be making use of all the time. But there are corner cases where its useful.


Existing solutions (unsatisfactory)

Noting existing (poor) solutions, which work at some level but am not satisfied with (which is why Im asking the question)

Using 2 enums

It works but isn't a significant advantage over defining the flags directly.

    enum {
        A_SHIFT,
        B_SHIFT,
        C_SHIFT,
    };

    enum {
        A = (1 << A_SHIFT),
        B = (1 << B_SHIFT),
        C = (1 << C_SHIFT),
    };

Using GCC's __COUNTER__

Major downside is that it only works with GCC, and relies on defining some unique starting value to use the enum... which pollutes the name-space.

#define ENUM_INC (1 << (__COUNTER__ - _FIRST))
    enum { _FIRST = __COUNTER__ + 1 };
    enum {
        A = ENUM_INC,
        B = ENUM_INC,
        C = ENUM_INC,
    };
#undef ENUM_INC
ideasman42
  • 42,413
  • 44
  • 197
  • 320
  • 1
    Even if there were a more convenient way, I'd argue that for the sake of being explicit about your intentions, listing each bit shift is better. – Sinkingpoint Feb 13 '15 at 21:39
  • You are an adventures person! You need to be really brave to letting compiler define values that are later used in ABI. – Valeri Atamaniouk Feb 13 '15 at 21:39
  • 4
    Something with macros? Use your calculator to figure out the hex value? - you only have to do it once. Without knowing your full use case, you could be spending a lot of time trying to fix a problem that isn't really a problem. Personally I'd just use the explicit shifts so I can see what was intended. – John3136 Feb 13 '15 at 21:40
  • You could also simply write the constants in binary for readability's sake, EX: `0b100` for `(1<<2)`. Though the latter makes it a bit more clear that it's bit-2 that's hot, the former I have to manually count. With your `__counter__` method, you've saved the developer some time, but you've made your code *much* less clear as a consumer; I have to work a lot harder to figure out what each flag is than with the explicit shift method. – aruisdante Feb 13 '15 at 21:40
  • 3
    I vote for hex `0x01, 0x02, 0x04, 0x08, 0x10 ...`. Any C programmer worth his/her salt understands the 1,2,4,8 pattern. – user3386109 Feb 13 '15 at 21:58
  • @user3386109 - the issue isn't weather developers will understand whats going on. if you define enum's explicitly where each flag needs to use a consecutive bit, theres always chance for human error. – ideasman42 Feb 13 '15 at 22:14
  • This is being marked as opinion based, I'm not sure how this is opinion based, it is either possible to create an `enum` _(with each member using a consecutive bit)_, or not. – ideasman42 Feb 13 '15 at 22:19
  • @ideasman42 Enums can have explicit values, or incrementing values. There is no built-in pattern generator for consecutive bits. So it's just a question of what's the best method for explicitly specifying the values. I say keep it simple and obvious. Your answer below takes the opposite approach, and takes it to an extraordinary extreme. BTW, I didn't vote to close. – user3386109 Feb 13 '15 at 22:27
  • @user3386109, Im not especially happy with my own answer, nevertheless, using such a macro puts all the complexity in one place, after that you can glance at `ENUM_FLAGS` and know with absolute certainty how it works without having to check the values. While its not a common problem. I've seen cases where developers overlap bits accidentally (merging branches, when enums are are grouped awkwardly, out of order... etc), this kind of mistake isn't unheard of. – ideasman42 Feb 13 '15 at 22:30
  • @aruisdante: Binary literals like `0b100` are non-standard. – Keith Thompson Feb 13 '15 at 22:41
  • In `enum { A = 0, B, C, };`, the `= 0` is superfluous; the first enumeration is always zero unless a different value is specified. – Keith Thompson Feb 13 '15 at 22:42
  • 1
    The votes to close are likely because in your original question you don't mention that your goal is to replace the standard enum incrementor `0->n` with the one-hot equivalent `2^0->2^n`. As phrased it looks more like you simply don't like the syntax of `(1< – aruisdante Feb 13 '15 at 22:49
  • @KeithThompson whoopse, you're absolutely right, I've been in Python/Java land too long today. – aruisdante Feb 13 '15 at 22:51
  • That counter macro looks worse than the code it was designed to replace. Whoever uses it should re-evaluate their priorities. – SevenBits Feb 13 '15 at 23:00
  • @SevenBits, right - the `__COUNTER__` example was given as an example of an unsatisfactory solution. Just to show that it is possible to do. – ideasman42 Feb 13 '15 at 23:02

2 Answers2

4

If you're willing to go beyond pure C, you can use a preprocessing filter to generate some of the code for you.

For example, here's a simple Perl script:

#!/usr/bin/perl

use strict;
use warnings;

my $flag_bit = 0;

while (<>) {
    if (/\@RESET_FLAG\@/) {
        s/\@RESET_FLAG\@//;
        $flag_bit = 0;
    }
    elsif (/\@FLAG\@/) {
        s/\@FLAG\@/sprintf("0x%x", 1 << $flag_bit)/e;
        $flag_bit ++;
    }
    print;
}

that translates this:

enum foo {
    A = @FLAG@,
    B = @FLAG@,
    C = @FLAG@,
};

to this:

enum foo {
    A = 0x1,
    B = 0x2,
    C = 0x4,
};

The @RESET_FLAG@ syntax, not used in this example, lets you reset the flag value if you want to use this with more than one enumeration type.

You can include this in your build process by, for example, naming your C source file foo.c.in, and generating foo.c from foo.c.in by running the filter on it.

Note that the filter doesn't handle more than one use of @...@ on a line, and it's not going to behave well if the number of flags exceeds the number of bits Perl thinks are in an integer. It also expands @FLAG@ in comments (shouldn't be a problem) and string literals (which might be). Bells, whistles, and error checking are left as an exercise.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
2

Using the macro below, you can do this.

enum { ENUM_FLAGS(A, B, C) };

While the macro is rather verbose, calling it isn't, and this may be a reasonable alternative to generating headers at build time.

Works modern compilers - GCC/Clang/MSVC/IntelC++.


/** generated with:
 * \code{.py}
 * enum_flag_bits = 32
 * for i in range(enum_flag_bits):
 *     args = [(chr(ord('a') + (c % 26)) + (chr(ord('0') + (c // 26)))) for c in range(i + 1)]
 *     print("#define _VA_ENUM_FLAGS_%d(%s) \\\n        " % (i + 1, ", ".join(args)), end="")
 *     if i != 0:
 *         print("_VA_ENUM_FLAGS_%d(%s) " % (i, ", ".join(args[:-1])), end="")
 *     print("%s = %s," % (args[-1], "(1u << %d)" % i))
 * \endcode
 */
#define _VA_ENUM_FLAGS_1(a0) \
        a0 = (1u << 0),
#define _VA_ENUM_FLAGS_2(a0, b0) \
        _VA_ENUM_FLAGS_1(a0) b0 = (1u << 1),
#define _VA_ENUM_FLAGS_3(a0, b0, c0) \
        _VA_ENUM_FLAGS_2(a0, b0) c0 = (1u << 2),
#define _VA_ENUM_FLAGS_4(a0, b0, c0, d0) \
        _VA_ENUM_FLAGS_3(a0, b0, c0) d0 = (1u << 3),
#define _VA_ENUM_FLAGS_5(a0, b0, c0, d0, e0) \
        _VA_ENUM_FLAGS_4(a0, b0, c0, d0) e0 = (1u << 4),
#define _VA_ENUM_FLAGS_6(a0, b0, c0, d0, e0, f0) \
        _VA_ENUM_FLAGS_5(a0, b0, c0, d0, e0) f0 = (1u << 5),
#define _VA_ENUM_FLAGS_7(a0, b0, c0, d0, e0, f0, g0) \
        _VA_ENUM_FLAGS_6(a0, b0, c0, d0, e0, f0) g0 = (1u << 6),
#define _VA_ENUM_FLAGS_8(a0, b0, c0, d0, e0, f0, g0, h0) \
        _VA_ENUM_FLAGS_7(a0, b0, c0, d0, e0, f0, g0) h0 = (1u << 7),
#define _VA_ENUM_FLAGS_9(a0, b0, c0, d0, e0, f0, g0, h0, i0) \
        _VA_ENUM_FLAGS_8(a0, b0, c0, d0, e0, f0, g0, h0) i0 = (1u << 8),
#define _VA_ENUM_FLAGS_10(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0) \
        _VA_ENUM_FLAGS_9(a0, b0, c0, d0, e0, f0, g0, h0, i0) j0 = (1u << 9),
#define _VA_ENUM_FLAGS_11(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0) \
        _VA_ENUM_FLAGS_10(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0) k0 = (1u << 10),
#define _VA_ENUM_FLAGS_12(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0) \
        _VA_ENUM_FLAGS_11(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0) l0 = (1u << 11),
#define _VA_ENUM_FLAGS_13(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0) \
        _VA_ENUM_FLAGS_12(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0) m0 = (1u << 12),
#define _VA_ENUM_FLAGS_14(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0) \
        _VA_ENUM_FLAGS_13(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0) n0 = (1u << 13),
#define _VA_ENUM_FLAGS_15(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0) \
        _VA_ENUM_FLAGS_14(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0) o0 = (1u << 14),
#define _VA_ENUM_FLAGS_16(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0) \
        _VA_ENUM_FLAGS_15(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0) p0 = (1u << 15),
#define _VA_ENUM_FLAGS_17(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0) \
        _VA_ENUM_FLAGS_16(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0) q0 = (1u << 16),
#define _VA_ENUM_FLAGS_18(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0) \
        _VA_ENUM_FLAGS_17(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0) r0 = (1u << 17),
#define _VA_ENUM_FLAGS_19(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0) \
        _VA_ENUM_FLAGS_18(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0) s0 = (1u << 18),
#define _VA_ENUM_FLAGS_20(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0) \
        _VA_ENUM_FLAGS_19(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0) t0 = (1u << 19),
#define _VA_ENUM_FLAGS_21(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0) \
        _VA_ENUM_FLAGS_20(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0) u0 = (1u << 20),
#define _VA_ENUM_FLAGS_22(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, v0) \
        _VA_ENUM_FLAGS_21(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0) v0 = (1u << 21),
#define _VA_ENUM_FLAGS_23(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, v0, w0) \
        _VA_ENUM_FLAGS_22(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, v0) w0 = (1u << 22),
#define _VA_ENUM_FLAGS_24(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, v0, w0, x0) \
        _VA_ENUM_FLAGS_23(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, v0, w0) x0 = (1u << 23),
#define _VA_ENUM_FLAGS_25(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, v0, w0, x0, y0) \
        _VA_ENUM_FLAGS_24(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, v0, w0, x0) y0 = (1u << 24),
#define _VA_ENUM_FLAGS_26(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, v0, w0, x0, y0, z0) \
        _VA_ENUM_FLAGS_25(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, v0, w0, x0, y0) z0 = (1u << 25),
#define _VA_ENUM_FLAGS_27(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, v0, w0, x0, y0, z0, a1) \
        _VA_ENUM_FLAGS_26(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, v0, w0, x0, y0, z0) a1 = (1u << 26),
#define _VA_ENUM_FLAGS_28(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, v0, w0, x0, y0, z0, a1, b1) \
        _VA_ENUM_FLAGS_27(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, v0, w0, x0, y0, z0, a1) b1 = (1u << 27),
#define _VA_ENUM_FLAGS_29(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, v0, w0, x0, y0, z0, a1, b1, c1) \
        _VA_ENUM_FLAGS_28(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, v0, w0, x0, y0, z0, a1, b1) c1 = (1u << 28),
#define _VA_ENUM_FLAGS_30(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, v0, w0, x0, y0, z0, a1, b1, c1, d1) \
        _VA_ENUM_FLAGS_29(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, v0, w0, x0, y0, z0, a1, b1, c1) d1 = (1u << 29),
#define _VA_ENUM_FLAGS_31(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1) \
        _VA_ENUM_FLAGS_30(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, v0, w0, x0, y0, z0, a1, b1, c1, d1) e1 = (1u << 30),
#define _VA_ENUM_FLAGS_32(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1) \
        _VA_ENUM_FLAGS_31(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1) f1 = (1u << 31),

/* reusable ENUM_FLAGS macro */
#define ENUM_FLAGS(...) VA_NARGS_CALL_OVERLOAD(_VA_ENUM_FLAGS_, __VA_ARGS__)

Uses this VA_NARGS_CALL_OVERLOAD as defined here:


/* varargs macros (keep first so others can use) */
/* --- internal helpers --- */
#define _VA_NARGS_GLUE(x, y) x y
#define _VA_NARGS_RETURN_COUNT(\
    _1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, _9_, _10_, _11_, _12_, _13_, _14_, _15_, _16_, \
    _17_, _18_, _19_, _20_, _21_, _22_, _23_, _24_, _25_, _26_, _27_, _28_, _29_, _30_, _31_, _32_, \
    _33_, _34_, _35_, _36_, _37_, _38_, _39_, _40_, _41_, _42_, _43_, _44_, _45_, _46_, _47_, _48_, \
    _49_, _50_, _51_, _52_, _53_, _54_, _55_, _56_, _57_, _58_, _59_, _60_, _61_, _62_, _63_, _64_, \
    count, ...) count
#define _VA_NARGS_EXPAND(args) _VA_NARGS_RETURN_COUNT args
/* 64 args max */
#define _VA_NARGS_COUNT(...) _VA_NARGS_EXPAND((__VA_ARGS__, \
    64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, \
    48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, \
    32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \
    16, 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2, 1, 0))
#define _VA_NARGS_OVERLOAD_MACRO2(name, count) name##count
#define _VA_NARGS_OVERLOAD_MACRO1(name, count) _VA_NARGS_OVERLOAD_MACRO2(name, count)
#define _VA_NARGS_OVERLOAD_MACRO(name,  count) _VA_NARGS_OVERLOAD_MACRO1(name, count)
/* --- expose for re-use --- */
#define VA_NARGS_CALL_OVERLOAD(name, ...) \
    _VA_NARGS_GLUE(_VA_NARGS_OVERLOAD_MACRO(name, _VA_NARGS_COUNT(__VA_ARGS__)), (__VA_ARGS__))
ideasman42
  • 42,413
  • 44
  • 197
  • 320
  • 7
    All I could think as I scrolled through this was: Why. – Selali Adobor Feb 13 '15 at 22:40
  • @Assorted Trailmix, completely understandable. I only gave this answer because it is _a_ solution - almost certainly one you wouldn't want to use 99% of the time when defining a typical enum flags. However there are corner-cases if you have to define many enums with the constraints outlined in the question, This does save you from the risk of human error. Another solution in that case could be to generate headers at build time. But having to generate headers is a hassle - in comparison a macro like this isn't _necessarily_ so bad. – ideasman42 Feb 13 '15 at 23:11