简体   繁体   中英

C check array content at compile time

I have an enum and an array of a structure. The first field in the structure is of the enum type. Now I want to check at compile time, if the value in the first field of the first array element is the same as the first value of the enum type. Same with the second element and so on...

Basically like this:

typedef enum {
   A = 0,
   B,
   C
} enumerator1;

typedef struct {
   enumerator1   a;
   unsigned char foo;
   unsigned char bar;
} structure1;

const structure1 array1[3] =
{
   {A, 1, 1},   //This element should contain A
   {C, 1, 1},   //This element should contain B
   {B, 1, 1}    //This element should contain C
};

In the example above, B and C are swapped and I would like to catch this at compile time. What I am looking for is something like this:

#if array1[0].a != A
#error
#endif

But that's not working, compiler says "token "[" is not valid in preprocessor expressions". I also tried it with something like this:

typedef unsigned char Check[(array1[0].a != A) ? 1 : -1];

But with the same result. How, if possible, can I implement such a check?

Thank you.

You can't. Arrays in C are conceptually a runtime thing. There's no portable way to force a comptime assertion on that.

That doesn't mean an optimizing compiler won't see into it.

If I do if(!(array1[0].a == A)) abort(); and look at the disassembly, I can see that both gcc and clang are completely eliding this code when I compile with optimization on.

There is a GCC trick that allows you to turn this optimizer knowledge into a comptime assertion (or an ASAP assertion as I call it).

#if __GNUC__ && !__clang__
#pragma GCC diagnostic error "-Walloc-size-larger-than=999999999L"
#endif

#if NDEBUG
enum { ndebug=1};
#else
enum { ndebug=0};
#endif

#include <assert.h>
#define ASAP_ASSERT(X)  \
        do{  \
            /*if possible, statically assert it even without the optimizer*/ \
            (void)(__builtin_constant_p(X) ? sizeof(int[(X)?1:-1]) : 0); \
            _Bool ASAP_ASSERT=(X); /*prevent double evaluation*/ \
            if(!ndebug){ \
                /*try to make the optimizer raise a -Walloc-size-larger-than=*/ \
                _Bool volatile ASAP_ASSERT__[(ASAP_ASSERT)?1:-1]; \
                ASAP_ASSERT__[0]=0; \
                (void)ASAP_ASSERT__; \
            } \
            assert(ASAP_ASSERT); /*if all static assert attempts fail, do it dynamically*/ \
        }while(0)

typedef enum {
   A = 0,
   B,
   C
} enumerator1;

typedef struct {
   enumerator1   a;
   unsigned char foo;
   unsigned char bar;
} structure1;

const structure1 array1[3] =
{
   {A, 1, 1},   //This element should contain A
   {C, 1, 1},   //This element should contain B
   {B, 1, 1}    //This element should contain C
};
#include <stdlib.h>
int main()
{

    ASAP_ASSERT(array1[0].a!=A); //will cause a comptime failure on gcc when compiled with at least -O1
}

The downsides are that it's GCC specific, has a small runtime cost (the volatile write, which you can turn off by setting ndebug to 1, but then you won't get the compile time failure) and I've had some false positives with it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM