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

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

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

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

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

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

Ответы [ 20 ]

45 голосов
/ 22 декабря 2009

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

По глубине:

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

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

27 голосов
/ 22 декабря 2009

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

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

Или менее фотогеничный, но и менее предупреждающий:

do { stuff(); } while ((void)0,0)
17 голосов
/ 27 января 2011

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

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

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

15 голосов
/ 04 мая 2013

У меня есть шаблон, который я основал здесь на ответе, и он работает на 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();

Редактировать: Несколько лет спустя я понял, что забыл добавить шаблон, для которого я на самом деле написал этот макрос - шаблон "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, которая является более структурированной, чем грубое переход к вашему коду очистки и возврата. Использование этого макроса ONCE вместо while (0) выключает VS.

4 голосов
/ 11 сентября 2014

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

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

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

3 голосов
/ 16 апреля 2016

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

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

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

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

3 голосов
/ 19 июля 2016

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

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

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

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

2 голосов
/ 20 августа 2012

Предупреждение связано с 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  

Просто вставьте свой код между НАЧАЛО и КОНЕЦ.

2 голосов
/ 24 августа 2016

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

do {
    // Anything you like
} WHILE_FALSE;

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

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

Проверено на MSVC ++ 2013.

1 голос
/ 22 декабря 2009

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

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

...