В списке рассылки ядра Linux было обсуждение макроса, который проверяет, является ли его аргумент целочисленным постоянным выражением и является ли само целочисленное постоянное выражение.
Один особенно умный подход, который не использует встроенные функции, предложено Мартином Юккером (беря вдохновение из tgmath.h из glibc):
#define ICE_P(x) (sizeof(int) == sizeof(*(1 ? ((void*)((x) * 0l)) : (int*)1)))
Этот макрос заменяется целочисленным постоянным выражением значения 1, если аргумент является целочисленным постоянным выражением, в противном случае - 0. Однако он полагается на то, что sizeof(void) разрешен (и отличается от sizeof(int)), который является Расширение GNU C.
Можно ли написать такой макрос без встроенных функций и без использования языковых расширений? Если да, оценивает ли он свой аргумент?
Для объяснение макроса, показанного выше, см. Вместо этого: Макрос __is_constexpr ядра Linux
@WeatherVane Это не вопрос отладки («почему этот код не работает?»).
@melpomene, почему это исключено? Я хотел бы посмотреть, как это используется.
@WeatherVane См. ваша ссылка: "Когда задаете вопрос о проблеме, вызванной вашим кодом ..." Здесь не применяется.
@melpomene Я бы избегал "умных" подходов и все же хотел бы увидеть некоторый контекст.
@WeatherVane Пример: lkml.org/lkml/2018/3/21/223; в основном, цель состоит в том, чтобы определить, например, макрос MAX, который безопасен с эффективными аргументами и является постоянным выражением с постоянными аргументами. Использование встроенной функции или временных переменных выполняет №1, но не №2; с использованием ?: и повторением выражений аргументов выполняется №2, но не №1. Если бы вы могли обнаружить постоянные выражения, вы могли бы съесть свой торт и тоже его съесть.
Есть что-то в том, что char * и void * имеют одинаковые / совместимые представления, поэтому, возможно, используйте (char*)(void*)((x), чтобы избежать sizeof(void).
@chux, который не скомпилируется, поскольку условный оператор требует, чтобы один операнд был неявно преобразован в тип другого





Используйте ту же идею, где тип выражения ?: зависит от того, является ли аргумент константой нулевого указателя или обычным void *, но определите тип с помощью _Generic:
#define ICE_P(x) _Generic((1? (void *) ((x)*0) : (int *) 0), int*: 1, void*: 0)
Демо на Ideone._Generic - это дополнение C11, поэтому, если вы застряли на C99 или чем-то более раннем, вы не сможете его использовать.
Также есть стандартные ссылки для определение константы нулевого указателя и способ взаимодействия констант нулевого указателя с типом выражения ?::
An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.
а также
If both the second and third operands are pointers or one is a null pointer constant and the other is a pointer, the result type is a pointer to a type qualified with all the type qualifiers of the types referenced by both operands. Furthermore, if both operands are pointers to compatible types or to differently qualified versions of compatible types, the result type is a pointer to an appropriately qualified version of the composite type; if one operand is a null pointer constant, the result has the type of the other operand; otherwise, one operand is a pointer to void or a qualified version of void, in which case the result type is a pointer to an appropriately qualified version of void.
Действительно, _Generic решает эту проблему (аналогично использованию встроенного __builtin_types_compatible_p). Ядро не использует C11, но я не пометил вопрос как C89, чтобы получить как можно больше решений в разных версиях Стандарта. Посмотрим, предложит ли кто-нибудь другой подход!
Возможно, также стоит добавить ссылки для объяснения проблемы с sizeof(void): §6.5.3.4¶1 и §6.2.5¶19. Хотя все это, вероятно, должно быть в вопросе, а не в вашем ответе ...
@Acorn: Я не понял ничего, что работает в C89. Все, что я могу придумать, требует C++ или расширений компилятора - таких как decltype, typeof, SFINAE, перегрузка функций, специализация шаблонов и т. д. Кроме того, довольно неудобно, что трюк с ?:, кажется, позволяет вам выбирать между void * или каким-либо другим типом указателя. ; Если бы мы могли заставить его выбирать между двумя типами указателей на функции, мы могли бы проверить возвращаемое значение sizeof - или что-то более простое, которое я упустил, если бы мы могли выбирать между двумя типами указателей на объекты, мы могли бы просто разыменовать и sizeof.
Вы не показали использование макроса. Пожалуйста, опубликуйте Минимальный, полный и проверяемый пример, который показывает проблему.