Использование препроцессорных условных выражений в макрофункциях с двойными скобками


Дано:

#define TRACE(x) do { if (DEBUG) dbg_print x; } while (0)

Я хочу:

TRACE((
    "Message: %s"
#ifdef MYDEF
    "Additional stuff"
#endif
    , msg));

Но дает ошибку:

error C2121: '#' invalid character : possibly the result of a macro expansion
error C2146: syntax error : missing ')' before identifier 'ifdef'
error C2121: '#' invalid character : possibly the result of a macro expansion
error C2059: syntax error : ')'
Я знаю, что могу легко решить эту проблему, написав два разных вызова TRACE и используя #ifdef...#else...#endif, но это только упрощенный случай. Мой фактический случай использования включает в себя несколько #ifdef, которые управляют как строкой формата, так и аргументами, поэтому не практично писать несколько вызовов трассировки (например, с 3 ifdef, мне понадобится 2^3 = 8 различных вызовов, чтобы позаботиться обо всех возможных комбинациях). Есть ли способ обойти это? вот это?
1 2

1 ответ:

Некоторые компиляторы будут компилировать ваш код через расширение компилятора, но он не является переносимым. Вы можете обойти эту проблему, условно определив другой макрос и используя его результат в вызове TRACE, например:

#ifdef MYDEF
#define IF_MY(x,y) x y
#else
#define IF_MY(x,y) x
#endif

Теперь вы можете написать свой TRACE следующим образом:

TRACE((IF_MY("Message: %s", "Additional stuff"), msg));

Демо №1.

Похоже, что он столкнется с проблемами масштабируемости.

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

#ifdef YOURDEF
#define IF_MY_YOUR(x,y,z) IF_MY(x,y) z
#else
#define IF_MY_YOUR(x,y,z) IF_MY(x,y)
#endif

Теперь вы можете использовать комбинированный макрос в вашем TRACE:

TRACE((IF_MY_YOUR("Message: %s", "Additional stuff", "More stuff"), msg));

Demo #2 - оба MYDEF и YOURDEF определены

Demo #2-только YOURDEF определено

Demo #2-только MYDEF определено

Демо #2-ни MYDEF, ни YOURDEF не определено

Что, если у меня есть дополнительные аргументы, которые также должны контролироваться #define?

Сделайте то же самое для параметров, с запятыми между частями x и y. Вызов TRACE будет выглядеть так:

TRACE((
    IF_MY_YOUR_FMT("Message: %s", "Additional %s stuff", "More %s stuff")
,   IF_MY_YOUR_ARG(msg1, msg2, msg3)
));