C проверять содержимое массива во время компиляции - PullRequest
0 голосов
/ 11 февраля 2019

У меня есть enum и массив структуры.Первое поле в структуре имеет тип enum.Теперь я хочу проверить во время компиляции, совпадает ли значение в первом поле первого элемента массива с первым значением типа enum.То же самое со вторым элементом и так далее ...

В основном так:

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
};

В примере выше, B и C поменялись местами, и я хотел бы поймать это во время компиляции,Я ищу что-то вроде этого:

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

Но это не работает, компилятор говорит, что «токен» [«недопустим в выражениях препроцессора».Я тоже попробовал что-то вроде этого:

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

Но с тем же результатом.Как, если возможно, я могу осуществить такую ​​проверку?

Спасибо.

1 Ответ

0 голосов
/ 11 февраля 2019

Вы не можете.Массивы в Си концептуально являются вещью времени выполнения.Нет переносимого способа навязать ему утверждение comptime.

Это не означает, что оптимизирующий компилятор не увидит его.

Если я сделаю if(!(array1[0].a == A)) abort(); и посмотрю на разборкуЯ вижу, что и gcc, и clang полностью исключают этот код, когда я компилирую с оптимизацией.

Существует трюк GCC, позволяющий вам превратить эти знания оптимизатора в утверждение времени выполнения (или утверждение ASAP какЯ называю это).

#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
}

Недостатком является то, что он специфичен для GCC, имеет небольшие затраты времени выполнения (энергозависимая запись, которую можно отключить, установив ndebug в 1, но тогда вы не получитесбой времени компиляции) и у меня были некоторые ложные срабатывания.

...