C/С++: как использовать do-while (0); строить без предупреждений компилятора, таких как C4127?

Я часто использую do-while (0) конструкцию в своих #defines по причинам, описанным в в этом ответе. Также я пытаюсь использовать как можно более высокий уровень предупреждения от компилятора, чтобы уловить больше потенциальных проблем и сделать мой код более надежным и кросс-платформенным. Поэтому я обычно использую -Wall с gcc и /Wall с MSVC.

К сожалению, MSVC жалуется на конструкцию do-while (0):

foo.c(36) : warning C4127: conditional expression is constant

Что мне делать с этим предупреждением?

Просто отключите его глобально для всех файлов? Для меня это не кажется хорошей идеей.

Ответ 1

Сводка: Это предупреждение (C4127) в данном конкретном случае является тонкой ошибкой компилятора. Не стесняйтесь отключить его.

В глубине:

Предполагалось уловить ситуации, когда логическое выражение оценивается константой в не очевидных ситуациях (например, if(a==a && a!=a), и каким-то образом оно превратило while(true) и другие полезные конструкции в недопустимые.

Корпорация Майкрософт рекомендует использовать for(;;) для бесконечного цикла, если вы хотите включить это предупреждение, и нет решения для вашего дела. Это одно из очень немногих предупреждений уровня 4, которые могут быть отключены нашими соглашениями о развитии компании.

Ответ 2

Возможно, вашему коду нужно больше совов:

do { stuff(); } while (0,0)

Или менее фотогенное, но и меньшее предупреждение:

do { stuff(); } while ((void)0,0)

Ответ 3

Как Майкл Барр отметил в Carl Smotricz ответить, для Visual Studio 2008+ вы можете использовать __ pragma:

#define MYMACRO(f,g)              \
  __pragma(warning(push))         \
  __pragma(warning(disable:4127)) \
  do { f; g; } while (0)          \
  __pragma(warning(pop))

Вы можете поместить его на одну строку (без \ s), если вы предпочитаете, чтобы макросы были нечитаемыми.

Ответ 4

У меня есть шаблон, на котором я основывал ответ здесь, и он работает на clang, gcc и MSVC. Я размещаю его здесь в надежде, что он будет полезен другим, и потому, что ответы здесь помогли мне сформулировать его.

#ifdef WIN32
#  define ONCE __pragma( warning(push) ) \
               __pragma( warning(disable:4127) ) \
               while( 0 ) \
               __pragma( warning(pop) )
#else
#  define ONCE while( 0 )
#endif

И я использую его следующим образом:

do {
   // Some stuff
} ONCE;

Вы также можете использовать это в макросах:

void SomeLogImpl( const char* filename, int line, ... );    

#ifdef NDEBUG
#  define LOG( ... )
#else
#  define LOG( ... ) do { \
      SomeLogImpl( __FILE__, __LINE__, __VA_ARGS__ ); \
   } ONCE
#endif

Это также работает для случая, указанного выше, если F использует "ONCE" в функции:

#define F( x ) do { f(x); } ONCE
...
if (a==b) F(bar); else someFunc();

Edit: Через несколько лет я понял, что забыл добавить шаблон, который я на самом деле написал для этого макроса, - шаблон "switch-like-a-goto":

do {
    begin_some_operation();

    if( something_is_wrong ) {
        break;
    }

    continue_big_operation();

    if( another_failure_cond ) {
        break;
    }

    finish_big_operation();
    return SUCCESS;
} ONCE;

cleanup_the_mess();
return FAILURE;

Это дает вам конструкцию try/finally-ish, которая более структурирована, чем крутой goto для вашего кода очистки и возврата. Использование этого ONCE макроса вместо while (0) отключает VS вверх.

Ответ 5

Используя более новые версии компилятора MS, вы можете использовать предупреждение:

#define MY_MACRO(stuff) \
    do { \
        stuff \
    __pragma(warning(suppress:4127)) \
    } while(0)

Вы также можете нажать/отключить/поп, но подавление - гораздо более удобный механизм.

Ответ 6

Вот еще один возможный подход, который позволяет избежать C4127, C4548 и C6319 (предупреждение анализа кода VS2013) и не требует макросов или прагм:

static const struct {
    inline operator bool() const { return false; }
} false_value;

do {
    // ...
} while (false_value);

Это оптимизируется и компилируется без предупреждений в GCC 4.9.2 и VS2013. На практике он может идти в пространстве имен.

Ответ 7

Предупреждение связано с while(false). Этот сайт дает пример того, как обойти эту проблему. Пример с сайта (вам придется переработать его для своего кода):

#define MULTI_LINE_MACRO_BEGIN do {  
#define MULTI_LINE_MACRO_END \  
    __pragma(warning(push)) \  
    __pragma(warning(disable:4127)) \  
    } while(0) \  
    __pragma(warning(pop))

#define MULTI_LINE_MACRO \  
        MULTI_LINE_MACRO_BEGIN \  
            std::printf("Hello "); \  
            std::printf("world!\n"); \  
        MULTI_LINE_MACRO_END  

Просто вставьте код между BEGIN и END.

Ответ 8

Эта ошибка компилятора была исправлена ​​в Visual Studio 2015 Update 1, даже если выпускает заметки не упоминает об этом.

Ошибка была объяснена в одном из предыдущих ответов:

Сводка: Это предупреждение (C4127) в данном конкретном случае является тонкой ошибкой компилятора. Не стесняйтесь отключить его.

Предполагалось уловить ситуации, когда логическое выражение оценивается константой в не очевидных ситуациях (например, if (a == a && a!= a), и каким-то образом она повернулась во время (истина) и другие полезные конструкции в недопустимые.

Ответ 9

Вы можете использовать

do {
    // Anything you like
} WHILE_FALSE;

И ранее определите макрос WHILE_FALSE следующим образом:

#define WHILE_FALSE \
    __pragma(warning(push))         \
    __pragma(warning(disable:4127)) \
    while(false)                    \
  __pragma(warning(pop))

Проверено на MSVС++ 2013.

Ответ 10

Этот "while (0)" материал является взломом и только что обернулся, чтобы укусить вас.

Предлагает ли ваш компилятор #pragma для выборочного и локального отключения определенных сообщений об ошибках? Если это так, это может быть разумной альтернативой.

Ответ 11

#define STUFF for (bool b = true; b;) do {f(); g(); b = false;} while (b)?

#define STUFF for (;;) {f(); g(); break;}?

Ответ 12

Вместо операторов do-while (0) можно использовать запятую вместо макрокоманды с несколькими операторами, которые будут использоваться в выражениях. Поэтому вместо:

#define FOO(...)    do { Statement1; Statement2; Statement3; } while(0)

Использование:

#define FOO(...)    (Statement1, Statement2, Statement3)

Это работает независимо от платформы и позволяет избежать предупреждения компилятора (даже если выбран самый высокий уровень предупреждения). Обратите внимание, что в запятой, содержащей макрос (второй FOO), результат последнего оператора (Statement3) будет результатом всего макроса.

Ответ 13

Я должен сказать, что я никогда не беспокоился о том, что делать... в макросах. Весь код в моих макросах сам включается в фигурные скобки, но без do -.. while. Например:

#define F(x) \
    {           \
        x++;    \
    }           \

int main() {
    int a = 1;
    F(a);
    printf( "%d\n", a );
}

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

Ответ 14

Вы можете использовать предупреждение #pragma для:

  • сохранить состояние
  • отключить предупреждение
  • написать код нарушения
  • вернуть предупреждение в предыдущее состояние

(вам нужно # перед прагмами, но SO с трудом справляется с ними и форматирует в одно и то же время)

#pragma warning( push )
#pragma warning( disable: 4127 )
// Your code
#pragma warning( pop ) 

Вы хотите нажать/вскрыть предупреждения, а затем отключить/включить, потому что вы не хотите вмешиваться в аргументы командной строки, которые могут быть выбраны для включения/выключения предупреждений (кто-то может использовать командную строку для выключения предупреждения, вы не хотите заставлять его снова... код выше имеет дело с этим).

Это лучше, чем отключить предупреждение во всем мире, так как вы можете контролировать его только для той части, которую вы хотите. Также вы можете сделать его частью макроса.

Ответ 15

Есть решение, но оно добавит больше циклов в ваш код. Не используйте явное значение в условии while.

Вы можете сделать это следующим образом:

file1.h

extern const int I_am_a_zero;
#define MY_MACRO(foo,bar) \
do \
{ \
} \
while(I_am_a_zero);

переменная I_am_a_zero должна быть определена в некотором .c файле.

В любом случае это предупреждение не отображается в GCC:)

См. этот связанный вопрос.

Ответ 16

Ну, для меня следующие работы без предупреждения C4127:

#define ALWAYS_TRUE(zzsome) ((##zzsome)==(##zzsome))

void foo()
{
    int a = 0;
    while( ALWAYS_TRUE(a) )
    {
    }
}

Конечно, компиляторы умны, а zzsome не должен быть постоянным

Ответ 17

Это отключит предупреждение, и компилятор все равно сможет оптимизировать код:

static inline bool to_bool(const bool v) { return v; }

if (to_bool(0)) { // no warning here
    dead_code(); // will be compiled out (by most compilers)
}

do { something(); } while(to_bool(0)); // no extra code generated

Ответ 18

Я нашел, что это самая короткая версия

do {
  // ... 
}
while (([]() { return 0; })())  /* workaround for MSVC warning C4172 : conditional expression is constant */

Не проверял, не оптимизирован ли он компилятором, но я бы предположил, что это так.

Ответ 19

Вы можете использовать цикл for как:

for (;;) {
  // code
  break;
}

Macro:

#define BEGIN \
  for (;;) {

#define END \
  break; }

Ответ 20

Я бы использовал

for(int i = 0; i < 1; ++i) //do once
{

}

Это эквивалентно

do
{
}while(0);

и не дает никаких предупреждений.