Clang: результат выражения не используется с тернарным оператором

Чтобы распечатать отладочные сообщения в моей программе, у меня есть это, которое можно использовать следующим образом:

DBG(5) << "Foobar" << std::endl;

5 означает уровень сообщения, если уровень отладки меньше 5, сообщение не будет напечатано. В настоящее время это реализовано так:

#define DBG(level) !::Logger::IsToDebug((level)) ? : ::Logger::Debug

В основном IsToDebug проверяет, должно ли сообщение быть напечатано, и возвращает true, когда это необходимо. Logger::Debug — это std::ostream. Это также работает с gcc и clang, однако clang генерирует неиспользуемые предупреждения результата выражения. Согласно этому письму это не нравится либо изменить.

Добавление к нему префикса (void) не работает, он будет приводить только перед ?, что приводит к ошибке компиляции (очевидно, что void не может быть преобразован в bool). Другая проблема с этим синтаксисом заключается в том, что он использует расширение gcc.

Выполнение таких действий, как #define DBG(x) if (::Logger::IsToDebug((x))) ::Logger::Debug, решает проблему, но это верный способ сломать вашу программу (if (foo) DBG(1) << "foo"; else ...) (и я не могу поместить все это в do { ... } while(0) из-за того, как вызывается макрос).

Единственное более или менее жизнеспособное решение, которое я придумал, это (при условии, что IsToDebug возвращает либо 0, либо 1):

#define DBG(level) for(int dbgtmpvar = ::Logger::IsToDebug((level)); \
                       dbgtmpvar > 0; --dbgtmpvar) ::Logger::Debug

Что кажется излишним (не считая накладных расходов во время выполнения)


person DirtY iCE    schedule 16.02.2012    source источник
comment
Правильно ли вы используете тернарный оператор? Где второе выражение?   -  person Nawaz    schedule 16.02.2012
comment
Похоже, это непреднамеренное использование троичного расширения gcc. Грязный айс, вероятно, думал, что ничего не поместит туда, но на самом деле он помещает туда логическое значение.   -  person Joe    schedule 16.02.2012
comment
Как я уже упоминал, это расширение gcc (которое clang также поддерживает), см. gcc.gnu.org/onlinedocs/gcc/Conditionals.html#Conditionals   -  person DirtY iCE    schedule 16.02.2012
comment
¤ Во-первых, обратите внимание, что использование условного оператора без второго выражения является нестандартным. Простой способ удалить вещи, зависящие от уровня отладки, состоит в том, чтобы определить DBG5(e) условно, чтобы он не расширялся до нуля для уровней отладки ниже 5. Для выражения просто определите в обычном C++ класс создателя строки S, затем используйте выражение, подобное DBG5( S() << "blah " << x );. . Ура и чт.,   -  person Cheers and hth. - Alf    schedule 16.02.2012
comment
@Joe похоже, я был настолько умен, что IsToDebug возвращает true, когда не должен печатать сообщение, я исправил пример. Поскольку << имеет более высокий приоритет, чем ?:, он будет интерпретировать его как (IsToDebug(x)) ? : (Debug << "Foobar" << std::endl); и не будет пытаться применить << к логическому значению.   -  person DirtY iCE    schedule 16.02.2012
comment
@AlfP.Steinbach AlfP.Steinbach Я не могу определить макрос условно, так как уровень указывается во время выполнения в командной строке программы. Проблема с выражением создания строки заключается в том, что этот макрос в настоящее время используется довольно много раз.   -  person DirtY iCE    schedule 16.02.2012


Ответы (1)


Я думаю, вам следует использовать тернарный оператор, как он определен в стандарте, а не расширение компилятора. Чтобы использовать стандартный тернарный оператор, вы также должны предоставить второе выражение. Для этого вы можете определить класс потока, производный от std::ostream, который никуда ничего не печатает. Объект такого класса можно использовать как второе выражение.

class oemptystream : std::ostream
{
     //..
};

extern oemptystream nout; //declaration here, as definition should go to .cpp

тогда

#define DBG(level) ::Logger::IsToDebug((level))? nout : ::Logger::Debug

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

nout << "message";

Или это,

::Logger::Debug << "message";

В любом случае, это примерно так:

std::cout << "message";

Поэтому я надеюсь, что это не должно давать предупреждения компилятору.

person Nawaz    schedule 16.02.2012
comment
на самом деле, оно сокращается либо до nout, либо до ::Logger::Debug << "message";. Оно сократилось бы до двух, о которых вы упомянули, если бы вы заключили выражение в скобках в макросе. Сказав это, например, использование std::cout вместо nout также будет работать, поскольку std::cout; само по себе ничего не делает. Использование просто false вместо nout похоже тоже работает, хотя я не уверен, что это стандарт. И даже это не вызывает предупреждения. - person DirtY iCE; 16.02.2012
comment
Обратите внимание, что вам не нужно наследовать от std::ostream: просто используйте std::ostream, который находится в состоянии отказа, например. std::ostream nout(0). Лично я не большой поклонник этого, потому что он по-прежнему оценивает выражения, передаваемые операторам вывода. То, что вы, вероятно, хотите, это что-то вроде !::Logger::IsToDebug((level)) || ::Logger::Debug. Это должно иметь правильный приоритет и предотвращать оценку выражений. - person Dietmar Kühl; 16.02.2012
comment
Думаю, я пойду с ::Logger::IsToDebug((level)) && ::Logger::Debug. Использование ! и || лучше, чем &&? - person DirtY iCE; 17.02.2012