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

В списке рассылки ядра 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

Вы не показали использование макроса. Пожалуйста, опубликуйте Минимальный, полный и проверяемый пример, который показывает проблему.

Weather Vane 25.03.2018 22:37

@WeatherVane Это не вопрос отладки («почему этот код не работает?»).

melpomene 25.03.2018 22:38

@melpomene, почему это исключено? Я хотел бы посмотреть, как это используется.

Weather Vane 25.03.2018 22:39

@WeatherVane См. ваша ссылка: "Когда задаете вопрос о проблеме, вызванной вашим кодом ..." Здесь не применяется.

melpomene 25.03.2018 22:40

@melpomene Я бы избегал "умных" подходов и все же хотел бы увидеть некоторый контекст.

Weather Vane 25.03.2018 22:45

@WeatherVane Пример: lkml.org/lkml/2018/3/21/223; в основном, цель состоит в том, чтобы определить, например, макрос MAX, который безопасен с эффективными аргументами и является постоянным выражением с постоянными аргументами. Использование встроенной функции или временных переменных выполняет №1, но не №2; с использованием ?: и повторением выражений аргументов выполняется №2, но не №1. Если бы вы могли обнаружить постоянные выражения, вы могли бы съесть свой торт и тоже его съесть.

melpomene 25.03.2018 22:45

Есть что-то в том, что char * и void * имеют одинаковые / совместимые представления, поэтому, возможно, используйте (char*)(void*)((x), чтобы избежать sizeof(void).

chux - Reinstate Monica 26.03.2018 02:42

@chux, который не скомпилируется, поскольку условный оператор требует, чтобы один операнд был неявно преобразован в тип другого

M.M 29.03.2018 10:56
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
36
8
8 982
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Используйте ту же идею, где тип выражения ?: зависит от того, является ли аргумент константой нулевого указателя или обычным 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, чтобы получить как можно больше решений в разных версиях Стандарта. Посмотрим, предложит ли кто-нибудь другой подход!

Acorn 25.03.2018 23:36

Возможно, также стоит добавить ссылки для объяснения проблемы с sizeof(void): §6.5.3.4¶1 и §6.2.5¶19. Хотя все это, вероятно, должно быть в вопросе, а не в вашем ответе ...

nemequ 25.03.2018 23:40

@Acorn: Я не понял ничего, что работает в C89. Все, что я могу придумать, требует C++ или расширений компилятора - таких как decltype, typeof, SFINAE, перегрузка функций, специализация шаблонов и т. д. Кроме того, довольно неудобно, что трюк с ?:, кажется, позволяет вам выбирать между void * или каким-либо другим типом указателя. ; Если бы мы могли заставить его выбирать между двумя типами указателей на функции, мы могли бы проверить возвращаемое значение sizeof - или что-то более простое, которое я упустил, если бы мы могли выбирать между двумя типами указателей на объекты, мы могли бы просто разыменовать и sizeof.

user2357112 supports Monica 25.03.2018 23:56

Другие вопросы по теме