Подтвердить что ты не робот

Назначение #define foo() do {} while (0)

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

#else /*CONFIG_PREEMPT*/
#define can_preempt_disable()      do { } while (0)
#define can_preempt_enable()       do { } while (0)
#endif /*CONFIG_PREEMPT*/

Я понимаю полезность

do { 
  ...;
  if(condition) break;
  ... 
} while (0); 

используя break как вид throw. Я полупонимаю обертывание последовательности функций, таких как

#define FOO() do { foo(); bar(); } while (0)

чтобы избежать оговорок с бесцельной if. Я понимаю, что иногда для #define требуются "операторы no-op". Но почему именно этот вид? в частности, пустые фигурные скобки, ложное состояние, делать... пока? Некоторые синтаксические оговорки, которые я не могу понять?

4b9b3361

Ответ 1

полный отрывок из соответствующего файла:

#if !defined(CONFIG_PREEMPT_RT) && ( defined(CONFIG_PREEMPT) ||
    (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) )
#define can_preempt_disable preempt_disable
#define can_preempt_enable preempt_enable
#else /*CONFIG_PREEMPT*/
#define can_preempt_disable() do { } while (0)
#define can_preempt_enable() do { } while (0)
#endif /*CONFIG_PREEMPT*/ 

Таким образом, первая часть - это код, который вы получаете, когда вы запрашиваете защиту от упреждения, иначе вы получите пустые, не-ничего, циклы.

Я предполагаю, что они написаны так по обычным причинам, то есть для обеспечения того, что макрос все еще является допустимым оператором.

В определении не должно быть конечной точки с запятой, так как это будет в коде с использованием этих, например эта функция, которая начинается:

int c_can_wakeup_tx(struct canchip_t *chip, struct msgobj_t *obj)
{
    can_preempt_disable(); 

    ...

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

ОБНОВЛЕНИЕ 2. Определение его на ; приводит к двойным точкам с запятой, что является уродливым, по крайней мере, на мой взгляд. Пустая цепочка {} будет работать, я думаю, но эта конструкция do/while еще более идиоматична, поскольку она часто используется в таких случаях.

ОБНОВЛЕНИЕ 3. Как указано в комментарии, пустая пара скобок не будет работать, так как после вызова вы не сможете поставить точку с запятой. Ааа. Спасибо!

Ответ 2

Это распространенный синтаксис для уведомления компилятора о том, что макрос должен рассматриваться как оператор, а не как выражение (заявления против выражений).

В этом случае компилятор предупредит вас, если вы попытаетесь использовать can_preempt_disable() в качестве выражения. Это означает, что мы принудительно проверяем время компиляции, когда can_preempt_disable() используется как оператор. Очень часто желательны проверки времени компиляции.