четверг, 21 февраля 2008 г.

Статическая гимнастика...

Как-то раз я ломал голову над тем, как же мне в чистом си проверить размер структуры в процессе компиляции программы. sizeof не работает в условиях #if. Тогда я почему-то так ничего и не придумал, а стоило бы поискать...

Оказалось, что статические утверждения без проблем реализуются и в си, причем несколькими способами.

Способ первый:

#define STATIC_ASSERT(exp) extern char __static_assert[(exp) ? 1 : -1]

Этот весьма универсальный способ можно использовать непосредственно за описанием структуры.

strict foo {...};
STATIC_ASSERT (sizeof (struct foo) == xxx);

Попытка объявить массив отрицательного размера вызывает массу ругани.

Встречаются и менее универсальные способы - например:

#define STATIC_ASSERT(exp) switch (0) { case 0: case ((exp) ? 1 : 0): break; }

Но такие ассерты ограничены в области применения. Их нельзя будет использовать за пределами функций. Думаю что так же не стоит пытаться оборачивать первый вариант традиционным для макросов do { } while (0), Это так же сократит возможности его использования.

Про статические утверждения все. Но есть еще кое что, что хотел бы рассказать.

Для большей наглядности вместо соответствующего стандарту си - assert(exp) создал STUB_ASSERT(exp, msg), который в случае нарушения ругается не просто выражениями, а целыми предложениями. Заодно можно держаться в ядре немного подальше от стандарта, чтобы не конфликтовать с ним.

Ну сделать с сообщением не проблема, проблема в том, как сделать так, чтобы в местах использования старой формы появлялись варнинги, не нарушающие сборку программы. Можно сказать мягкая форма устаревания макросов. И выход нашелся.

#define assert(exp) STUB_ASSERT((exp) && 'FOOF', #exp)

Выражение 'FOOF' является допустимой для gcc многосимвольной константой, на которую выдается предупреждение 'Page.c:44:38: warning: multi-character character constant'. Можно конечно побаловаться с другими вариантами предупреждающих выражений, но в любом случае большой наглядности на этапе компиляции добиться не получится. Предупреждение и без того весьма примечательное.