Если вы замените трюк typedef-array-trick на enum-trick, то вы получите что-то, что работает как с clang, так и с gcc:
#define CONDITION 1
#define TOKENPASTE(a, b) a ## b // "##" is the "Token Pasting Operator"
#define TOKENPASTE2(a,b) TOKENPASTE(a, b) // expand then paste
#define static_assert(x, msg) enum { TOKENPASTE2(ASSERT_line_,__LINE__) \
= 1 / (msg && (x)) }
static_assert( CONDITION, "This should pass");
static_assert(!CONDITION, "This should fail");
Это дает мне, например, gcc в строке 9 файла foo.c:
foo.c:9: warning: division by zero [-Wdiv-by-zero]
static_assert(!CONDITION, "This should fail");
^
foo.c:9: error: enumerator value for 'ASSERT_line_9' is not an integer constant
static_assert(!CONDITION, "This should fail");
^~~~~~~~~~~~~
(Здесь используется переключатель gcc -ftrack-macro-expansion=0
, поскольку дополнительные сообщения об ошибках не очень полезны и просто добавляют шум.)
Обратите внимание, что искажение имени по-прежнему необходимо, но вы его не указали. Здесь текст ASSERT_line_
объединяется с переменной __LINE__
. Это обеспечивает уникальное имя при условии:
- Вы не используете его дважды в одной строке.
- Вы не используете его в заголовочных файлах (или доверяете удаче).
- Ваш код не использует идентификаторы, такие как
ASSERT_line_9
в других местах.
Для заголовочных файлов вам нужно будет где-то добавить одно слово только с символами-идентификаторами. Например:
#define static_assert3(x, msg, file) enum { TOKENPASTE2(file,__LINE__) = \
1 / (msg && (x)) }
#define static_assert(x, msg) static_assert3(x, msg, my_header_h_)
Если в строке 17 произойдет сбой, gcc выдаст ошибку, такую как:
error: enumerator value for 'my_header_h_17' is not an integer constant
Альтернативой для искажения в заголовочных файлах является замена __LINE__
на __COUNTER__
. Я не использовал его, потому что он нестандартный и потому что Clang не спешил его принимать. Но сейчас он работает в gcc, msvc и clang около пяти лет.
Вы можете попробовать ту же модификацию с вашей идеей typedef-array и заменить оператор запятой на &&
. Тогда ваша ошибка gcc изменится на предупреждение. Например, изменив свой пример с Godbolt:
typedef char static_assert_2["hello world!" && (CONDITION) ? 1 : -1];
дает нежелательные warning: variably modified 'static_assert_2' at file scope
за GCC.