Обнаружение целых выражений целых чисел в макросах

В списке рассылки ядра Linux была обсуждена макрос, проверяющий, является ли его аргумент целочисленным константным выражением и является самим выражением константы.

Один особенно умный подход, который не использует встроенные функции, предложенный Мартином Уекером (взяв вдохновение из glibc tgmath.h):

#define ICE_P(x) (sizeof(int) == sizeof(*(1 ? ((void*)((x) * 0l)) : (int*)1)))

Этот макрос расширяется в целочисленное константное выражение значения 1, если аргумент представляет собой целочисленное константное выражение, 0 в противном случае. Тем не менее, он полагается на sizeof(void) для разрешения (и отличается от sizeof(int)), который является расширением GNU C.

Можно ли написать такой макрос без встроенных функций и не полагаться на языковые расширения? Если да, оценивает ли он свой аргумент?


Для объяснения макроса, показанного выше, см. вместо этого: Объяснить макрос, который проверяет выражение константы целых чисел

Ответ 1

Используйте ту же идею, где тип выражения ?: зависит от того, является ли аргумент константой с нулевым указателем или обычным void *, но обнаруживает тип с _Generic:

#define ICE_P(x) _Generic((1? (void *) ((x)*0) : (int *) 0), int*: 1, void*: 0)

Demo on Ideone. _Generic является добавлением C11, поэтому, если вы застряли на C99 или что-то раньше, вы не будете быть в состоянии использовать его.

Кроме того, у вас есть стандартные ссылки для определение константы нулевого указателя и способ взаимодействия констант нулевого указателя с типом выражения ?::

Целочисленное константное выражение со значением 0 или такое выражение, переданное типу void *, называется константой нулевого указателя.

и

Если и второй, и третий операнды являются указателями, а один - константой нулевого указателя, а другой - указателем, тип результата - это указатель на тип, соответствующий всем квалификаторам типов типов, на которые ссылаются оба операнда. Кроме того, если оба операнда являются указателями на совместимые типы или на разные версии совместимых типов, тип результата является указателем на подходящую версию составного типа; , если один операнд является константой нулевого указателя, результат имеет тип другого операнда; в противном случае один операнд является указателем на void или квалифицированной версией void, и в этом случае тип результата является указателем на подходящую версию void.

Ответ 2

У меня нет исправления для sizeof(void), не являющегося стандартным, но вы можете обойти возможность, что sizeof(void) == sizeof(int), выполнив что-то вроде:

#define ICE_P(x) ( \
  sizeof(void) != \
  sizeof(*( \
    1 ? \
      ((void*) ((x) * 0L) ) : \
      ((struct { char v[sizeof(void) * 2]; } *) 1) \
    ) \
  ) \
)

Я знаю, что это не полный ответ, но он немного ближе...