C/C++: How to use the do-while(0); construct witho

2019-01-13 09:52发布

问题:

I'm often use do-while(0) construct in my #defines, for the reasons described in this answer. Also I'm trying to use as high as possible warning level from compiler to catch more potential problem and make my code more robust and cross-platform. So I'm typically using -Wall with gcc and /Wall with MSVC.

Unfortunately MSVC complain about do-while(0) construct:

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

What should I do about this warning?

Just disable it globally for all files? It does not seems to be good idea for me.

回答1:

Summary: This warning (C4127) in this particular case is a subtle compiler bug. Feel free to disable it.

In depth:

It was meant to catch situations when logical expression evaluates to a constant in non-obvious situations (such as, if(a==a && a!=a), and somehow, it turned while(true) and other useful constructs into invalid.

Microsoft recommends using for(;;) for infinite loop if you want to have this warning on, and there is no solution for your case. This is one of very few Level-4 warnings my company's development conventions allow to disable.



回答2:

Perhaps your code needs more owls:

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

Or the less photogenic but also less warning producing:

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


回答3:

As Michael Burr noted in Carl Smotricz' answer, for Visual Studio 2008+ you can use __pragma:

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

You can put it on one line (without the \s) if you prefer macros to be unreadable.



回答4:

I have a pattern that I based off an answer here & it works on clang, gcc & MSVC. I'm posting it here in the hopes that it'll be useful for others & because the answers here helped me formulate it.

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

And I use it like this:

do {
   // Some stuff
} ONCE;

You can use this in macros too:

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

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

This also works for the case pointed out above, if F uses 'ONCE' in a function:

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

Edit: Years later, I realize I forgot to add the pattern I actually wrote this macro for - the "switch-like-a-goto" pattern:

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;

This gives you a try/finally-ish construct that's more structured than a crufty goto to your cleanup & return code. Using this ONCE macro instead of while(0) shuts VS up.



回答5:

Using newer versions of the MS compiler, you can use warning suppression:

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

You can also push/disable/pop, but suppress is a much more convenient mechanism.



回答6:

This compiler bug was fixed in Visual Studio 2015 Update 1, even if the releases notes don't mention it.

The bug was explained in one of the previous answers though:

Summary: This warning (C4127) in this particular case is a subtle compiler bug. Feel free to disable it.

It was meant to catch situations when logical expression evaluates to a constant in non-obvious situations (such as, if(a==a && a!=a), and somehow, it turned while(true) and other useful constructs into invalid.



回答7:

Here's another possible approach, which avoids C4127, C4548 and C6319 (VS2013 code analysis warning), and doesn't require macros or pragmas:

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

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

This optimises away, and compiles without warnings in GCC 4.9.2 and VS2013. In practice it could go in a namespace.



回答8:

The warning is due to the while(false). This site gives an example of how to workaround this problem. Example from site (you'll have to re-work it for your code):

#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  

Just insert your code between the BEGIN and END.



回答9:

You can use

do {
    // Anything you like
} WHILE_FALSE;

And earlier define WHILE_FALSE macro as follows:

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

Verified on MSVC++2013.



回答10:

This "while(0)" stuff is a hack and has just turned around to bite you.

Does your compiler offer #pragmas for selectively and locally turning off specific error messages? If so, that might be a sensible alternative.



回答11:

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

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



回答12:

You can use comma operator instead of do-while(0) construct for multi-statement macro to be used in expressions. So instead of:

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

Use:

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

This works independently from the platform and allows to avoid compiler warning (even if highest warning level is selected). Note that in comma containing macro (second FOO) the result of the last statement (Statement3) would be the result of entire macro.



回答13:

I must say, I've never bothered with the do..while construct in macros. All code in my macros is itself included in braces, but without the do-..while. For example:

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

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

Also, my own coding standard (and informal practice for years) has been to make all blocks, wherever they occur be enclosed in braces, which also more or less removes the problem.



回答14:

There is a solution but it will add more cycles to your code. Don't use explicit value in the while condition.

You can make it like this:

file1.h

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

the variable I_am_a_zero should be defined in some .c file.

Anyway this warning doesn't show up in GCC :)

See this related question.



回答15:

You can use #pragma warning to:

  1. save the state
  2. disable the warning
  3. write the offending code
  4. return the warning to their previous state

(you need a # before the pragmas, but SO is having a hard time dealing with them and formatting at the same time)

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

You want to push/pop the warnings rather then disable/enable because you do not want to interfere with the command line arguments that might be chosen to turn warnings on/off (someone may use the command line to turn off the warning, you do not want to force it back on... the code above deals with that).

This is better than turning the warning off globally since you can control it just for the part you want. Also you can make it part of the macro.



回答16:

Well, for me, the following works without the C4127 warning:

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

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

Ofcourse, compilers are smart and zzsome should not be a constant



回答17:

This will disable the warning and compiler will still be able to optimize the code:

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:

I found this to be the shortest version

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

Haven't checked to see if it is optimized away by the compiler, but I would guess it is.



回答19:

You could use for loop as:

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

Macro:

#define BEGIN \
  for (;;) {

#define END \
  break; }


回答20:

I'd use

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

}

This is equivalent to

do
{
}while(0);

and yields no warnings.