C/C++: How to use the do-while(0); construct without compiler warnings like C4127?

Viewed 13192

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.

21 Answers

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.

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)

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.

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.

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

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

You could use for loop as:

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

Macro:

#define BEGIN \
  for (;;) {

#define END \
  break; }

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.

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.

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.

Please use compiler switch /wd"4127" to disable this warning in your project.

Related