Home > Enterprise >  Can I get gcc/clang to warn me when an enum value isn't a key in an array?
Can I get gcc/clang to warn me when an enum value isn't a key in an array?

Time:01-08

I'm using an array to associate an enum with a value:

parse_node_category parse_node_categories[] = {
  [PT_ALL_EX_CALL] = PT_C_EXPRESSION,
  [PT_ALL_EX_FN] = PT_C_EXPRESSION,
  [PT_ALL_EX_FUN_BODY] = PT_C_EXPRESSION,
  ...
  [PT_ALL_MULTI_DATA_CONSTRUCTORS] = PT_C_NONE,
  [PT_ALL_MULTI_TYPE_CONSTRUCTOR_NAME] = PT_C_NONE,
  [PT_ALL_MULTI_DATA_CONSTRUCTOR_NAME] = PT_C_NONE,
  ...
};

I would like the compiler to check whether my array no longer maps every key to a value. Is there a way to do this?

I know I can do this with a switch, but I'm specifically looking to map keys to values via an array.

If I add a new value to my enum, the compiler should complain that parse_node_categories doesn't contain a value corresponding to the new enum value.

CodePudding user response:

We can almost do this with almost normal C, and maybe what we can do is better than nothing for your needs.

Let's say you have an enum and array like this:

enum {
    epic_foo,
    epic_bar
};

epic_value_t epic_mapping[] = {
    [epic_foo] = grand_maneuver,
    [epic_bar] = awesome_move
};

Well, we can add a special last element to the enum:

enum {
    epic_foo,
    epic_bar,
    /* add enum members above this line */
    epic_size
} epic_enum;

What's neat about this is that epic_size is now a constant expression with the value 2.

So when you add another value to the enum, if you just make sure to add it before the size placeholder,

enum {
    epic_foo,
    epic_bar,
    epic_qux,
    /* add enum members above this line */
    epic_size
} epic_enum;

and epic_size automatically becomes 3.

Okay, now what can we do with that?

Well, sizeof(epic_mapping) is also a constant expression. That value is in multiples of sizeof(epic_value_t), but we can do the normal "get number of elements in statically defined array" trick, and that's also a constant expression: sizeof(epic_mapping)/sizeof(epic_mapping[0]).

So let's just save that in a macro real quick:

#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0]))

Now if we could just check epic_size == ARRAY_SIZE(epic_mapping) at compile time, we'd have a decent solution - it wouldn't protect against holes in the array initialization, and it wouldn't work if we started giving the enum members custom values, but it would at least catch the case of adding entries to the end of the enum and then forgetting to add them to the array.

On modern C compilers, you probably have static_assert, so it's just a matter of adding

static_assert(epic_size == ARRAY_SIZE(epic_mapping))

after the array definition.

If you need to be portable to older C compilers, you can abuse some C rules to force a compile error depending on if a constant expression evaluates to true or false (because, for example, array sizes are allowed to be constant expressions and are never allowed to be negative):

#if __STDC_VERSION__ >= 201112L
#include <assert.h>  /* static_assert */
#else
#define static_assert(condition) sizeof(char[1 - 2*!!(condition)])
#endif

CodePudding user response:

Can I get gcc/clang to warn me when an enum value isn't a key in an array?

There is no such feature at this time.

You can write a plugin or create a patch for both gcc and clang and get such a feature merged.

Is there a way to do this?

Write your own static analyzer that checks that.

  • Related