Попросите компилятор проверить количество инициализаторов массива - программирование
Подтвердить что ты не робот

Попросите компилятор проверить количество инициализаторов массива

Инициализация массива (в С++, но любое решение, которое работает для C, вероятно, будет работать и здесь) с меньшим количеством инициализаторов, чем с элементами, совершенно законными:

int array[10] = { 1, 2, 3 };

Однако это может быть источником неясных ошибок. Есть ли способ, чтобы компилятор (gcc) проверял количество инициализаций для одного конкретного массива и выдавал предупреждение или даже ошибку, если объявленный и фактический размер не совпадают?

Я знаю, что могу использовать int array[] = { 1, 2, 3 };, а затем использовать статические утверждения, включающие sizeof(array), чтобы проверить мое ожидание. Но я использую array в других единицах перевода, поэтому я должен объявить его с явным размером. Так что этот трюк не будет работать для меня.

4b9b3361

Ответ 1

(продвигается из комментария по запросу)

Если значения в массиве важны для правильной функциональности системы, а с нулевыми значениями инициализации в конце вызывают ошибки, тогда я просто добавлю unit test, чтобы проверить, что массив содержит правильные данные, вместо этого попытки принудительно выполнить его в коде.

Ответ 2

Поскольку вы используете array в других единицах перевода, он, по-видимому, имеет внешнюю связь. В этом случае вам разрешено объявлять его дважды, если декларации дают один и тот же тип. Поэтому просто объявите его дважды, предоставив компилятору подсчет инициализаторов и задав размер. Поместите эту строку в один исходный файл, перед любым заголовком, объявляющим array:

int array[] = { 1, 2, 3 };

Позже в том же файле поместите строку #include, объявляющую array, с линией, такой как:

extern int array[10];

Если размеры массива различаются в двух объявлениях, компилятор сообщает об ошибке. Если они совпадают, компилятор примет их.

Ответ 3

У меня есть идея.

#define C_ASSERT(expr) extern char CAssertExtern[(expr)?1:-1]

#define NUM_ARGS__(X, \
                      N64,N63,N62,N61,N60, \
  N59,N58,N57,N56,N55,N54,N53,N52,N51,N50, \
  N49,N48,N47,N46,N45,N44,N43,N42,N41,N40, \
  N39,N38,N37,N36,N35,N34,N33,N32,N31,N30, \
  N29,N28,N27,N26,N25,N24,N23,N22,N21,N20, \
  N19,N18,N17,N16,N15,N14,N13,N12,N11,N10, \
  N09,N08,N07,N06,N05,N04,N03,N02,N01,  N, ...) N

#define NUM_ARGS(...) \
  NUM_ARGS__(0, __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 DECL_INIT_ARRAYN(TYPE, NAME, COUNT, N, ...) \
  C_ASSERT(COUNT == N); \
  TYPE NAME[COUNT] = { __VA_ARGS__ }

#define DECL_INIT_ARRAY(TYPE, NAME, COUNT, ...) \
  DECL_INIT_ARRAYN(TYPE, NAME, COUNT, NUM_ARGS(__VA_ARGS__), __VA_ARGS__)

DECL_INIT_ARRAY(const int, array3_3, 3, 1, 2, 3);

int main(void)
{
  DECL_INIT_ARRAY(const int, array5_4, 5, 1, 2, 3, 4);
  DECL_INIT_ARRAY(const int, array5_6, 5, 1, 2, 3, 4, 5, 6);
  return 0;
}

Выход (ideone):

prog.c: In function ‘main’:
prog.c:33:3: error: size of array ‘CAssertExtern’ is negative
prog.c:34:3: error: size of array ‘CAssertExtern’ is negative
prog.c:34:3: error: excess elements in array initializer [-Werror]
prog.c:34:3: error: (near initialization for ‘array5_6’) [-Werror]
prog.c:34:3: error: unused variable ‘array5_6’ [-Werror=unused-variable]
prog.c:33:3: error: unused variable ‘array5_4’ [-Werror=unused-variable]
prog.c:34:3: error: unused variable ‘CAssertExtern’ [-Werror=unused-variable]
cc1: all warnings being treated as errors

UPD: OP нашел более короткое решение С++ 11, основанное на той же идее использования __VA_ARGS__ и статического/компиляционного утверждения:

#include <tuple>

#define DECL_INIT_ARRAY(TYPE, NAME, COUNT, ...)                         \
  static_assert(COUNT ==                                                \
    std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value,     \
    "Array " #NAME " should have exactly " #COUNT " initializers");     \
  TYPE NAME[COUNT] = { __VA_ARGS__ }

DECL_INIT_ARRAY(const int, array3_3, 3, 1, 2, 3);

int main(void)
{
  DECL_INIT_ARRAY(const int, array5_4, 5, 1, 2, 3, 4);
  DECL_INIT_ARRAY(const int, array5_6, 5, 1, 2, 3, 4, 5, 6);
  return 0;
}

Выход (ideone):

prog.cpp: In function ‘int main()’:
prog.cpp:13:3: error: static assertion failed: Array array5_4 should have exactly 5 initializers
prog.cpp:14:3: error: static assertion failed: Array array5_6 should have exactly 5 initializers
prog.cpp:14:3: error: too many initializers for ‘const int [5]’
prog.cpp:13:3: warning: unused variable ‘array5_4’ [-Wunused-variable]
prog.cpp:14:3: warning: unused variable ‘array5_6’ [-Wunused-variable]