44

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.

Community
  • 1
  • 1
bialix
  • 20,053
  • 8
  • 46
  • 63

21 Answers21

46

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.

herohuyongtao
  • 49,413
  • 29
  • 133
  • 174
Pavel Radzivilovsky
  • 18,794
  • 5
  • 57
  • 67
  • I know about for(;;) but there it's not applicable as you said. I tend to follow your advice, just waiting for some other suggestions. – bialix Dec 22 '09 at 13:57
  • to me, for(;;) just looks ugly. – Pavel Radzivilovsky Dec 23 '09 at 09:13
  • Disabled at my company too (on a per-header basis actually). This thrashes the build output when using boost. – Alexandre C. Jan 27 '11 at 12:48
  • @alexandre When Using boost, you can just disable it around header includes. #include "warningsoff.h", then "warningson.h" to bring it back to desired level. – Pavel Radzivilovsky Jan 27 '11 at 23:43
  • 1
    This is what we do, but we manually put the pragmas before and after the boost headers. – Alexandre C. Jan 28 '11 at 08:15
  • I would not call that warning a compiler bug. It works as intended and as documented, and it occasionally finds a real bug. I prefer the `__pragma` solution to globally turning off a warning. – Adrian McCarthy Aug 20 '12 at 19:47
  • What about `if (1) {stuff;} else` without a trailing semicolon on the else? Or perhaps `do { stuff; if (always_true_condition) break; } while(1)`? Certainly `do ... while(1)` loops with internal `break` or `return` statements are pretty common, as are `if` statements with constant conditions. – supercat Sep 16 '13 at 17:30
  • In VS2015 it can be easily disabled from project properties/ property sheet -> "Common Properties -> C/C++ -> Advanced -> Disable Specific Warnings". There you can add the warnings that you want to be disabled, separated by semicolon. – mtb May 18 '16 at 13:44
28

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)
Community
  • 1
  • 1
Eric Seppanen
  • 5,923
  • 30
  • 24
18

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.

Community
  • 1
  • 1
Michel de Ruiter
  • 7,131
  • 5
  • 49
  • 74
  • 3
    The push is done too early here - you need to do the warning push immediately before the `while (0)` if you still want to catch issues in the `f` and `g` statements. See other MULTI_LINE_MACRO answer for something vaguely reusable. – Tom Whittock Jan 11 '15 at 15:46
15

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.

nevelis
  • 736
  • 6
  • 17
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.

Seth Moore
  • 51
  • 1
  • 3
3

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.

3

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.

james
  • 91
  • 3
2

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.

2

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.

Serge Rogatch
  • 13,865
  • 7
  • 86
  • 158
1

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.

1

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.

Carl Smotricz
  • 66,391
  • 18
  • 125
  • 167
  • Yes, MSVC supports pragmas, but how can I wrap macros itself in that pragma? – bialix Dec 22 '09 at 13:55
  • Sorry, don't know. This was an idea, a hint from me and I hoped it would inspire a solution. I don't remember dealing with this kind of problem before. – Carl Smotricz Dec 22 '09 at 13:58
  • C99 describes _Pragma() as an alternative to the pre-processor form #pragma precisely so it can be generated as part of a macro. Microsoft has declared that they're not interested in implementing C99, but I don't know if this feature is already there (given their front-end vendor). – Phil Miller Dec 22 '09 at 16:11
  • MSVC has the `__pragma()` preprocessor operator, which unfortunately is slightly different from C99's `_Pragma()` operator (C99's take a string literal, MSVC's takes tokens that aren't in a string): http://msdn.microsoft.com/en-us/library/d9x1s805.aspx – Michael Burr Jun 13 '10 at 15:59
1

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

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

ephemient
  • 198,619
  • 38
  • 280
  • 391
  • 1
    I hardly expect anybody using MSVC to be compiling plain C. – ephemient Dec 22 '09 at 16:22
  • 2
    OK, so I'm that crazy guy then. BTW, C extensions for Python usually compiled with MSVC on Windows. – bialix Dec 22 '09 at 17:56
  • +1 If you're compiling C++, that first macro is a nice in that it's portable and it doesn't resort to a global change to the build environment. The second one suffers from the dangling else problem that the do {} while (false) solution is designed to solve. – Adrian McCarthy Aug 20 '12 at 19:55
1

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.

Niklas R
  • 16,299
  • 28
  • 108
  • 203
1

You could use for loop as:

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

Macro:

#define BEGIN \
  for (;;) {

#define END \
  break; }
wolfram77
  • 2,841
  • 3
  • 23
  • 33
0

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.

  • 2
    do-while(0) is wide used construct AFAIK. – bialix Dec 22 '09 at 13:59
  • 4
    I know. I'm suggesting it is not necessary. –  Dec 22 '09 at 13:59
  • 1
    +1 It may be "widely" used (I've never seen it in production code, anyway) but I don't think it is necessary. After all macros should be avoided in the first place, so if you put them in, keep them simple. Don't introduce "loops" in my code. – Daniel Daranas Dec 22 '09 at 14:02
  • Yeah, it is widely used, but it's only really necessary in a few border cases that I tend to avoid anyway. In sane code, regular braces as shown here will usually suffice – jalf Dec 22 '09 at 14:12
  • I sometimes forget semicolons, and it makes the code horrible to read :/ – Johannes Schaub - litb Dec 22 '09 at 14:18
  • @litb Who are you talking to? If me, you can either use or omit the semicolon. And which bit of the code looks "horrible"? –  Dec 22 '09 at 14:21
  • 1
    @Tomas See my second paragraph. –  Dec 22 '09 at 14:45
  • 7
    @Neil your coding habit cannot be applied to everybody. so I think your answer is not a good guide. and some lines with trailing semi-colon and some lines without it is horrible. – CodingLab Dec 22 '09 at 17:13
0

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.

Ashwin Nanjappa
  • 76,204
  • 83
  • 211
  • 292
TofuBeer
  • 60,850
  • 18
  • 118
  • 163
  • I'm not sure how can I makew it part of the macro. If I put pragmas just after `#define foo(x) \ ` I've got error: error C2162: expected macro formal parameter – bialix Dec 22 '09 at 14:47
  • I don't have an MS compiler handy... so cannot test that. I guess wrap the macro usage in that code if you cannot put it in the macro. You are sure you did it as #pragma? (just checking :-) – TofuBeer Dec 22 '09 at 16:56
  • You can't put pragmas in the macro, so you'd have to disable the warning everywhere you __use__ the macro. Nasty. – Graeme Perrow Dec 22 '09 at 18:22
  • Bialix: Expand your macro to a call to an inline function. Put the do-while in this function and wrap it with pragma. – Ashwin Nanjappa Feb 24 '11 at 05:23
0

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.

Community
  • 1
  • 1
Yousf
  • 3,957
  • 3
  • 27
  • 37
  • Is not this const variable will prevent for optimization of the loop and will introduce unneeded check for zero? – bialix Dec 22 '09 at 17:54
  • Yes, that's why Yousf said it will add more cycles to your code. – jamesdlin Dec 22 '09 at 18:01
  • no, the compiler will not know that variable "I_am_a_zero" is actually a zero while compiling; Because it is an extern symbol which will be fulfilled in linking stage. – Yousf Dec 23 '09 at 08:03
0

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

0

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
wonder.mice
  • 7,227
  • 3
  • 36
  • 39
-1

I'd use

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

}

This is equivalent to

do
{
}while(0);

and yields no warnings.

Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
  • 10
    You missed the point, the `do { } while(0)` note the **lack** of semi colon, is used in macros [for a reason](http://stackoverflow.com/q/154136/3848) – Motti Sep 09 '13 at 09:30
-1

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

  • OP specifically turned on all warnings, so why suggest disabling? That would defeat the purpose of enabling the warning in the first place. – ChrisMM Feb 03 '20 at 10:18
  • That, again, defeats the purpose of turning it on in the first place. There may be many other places where OP needs the check to be on in MSVC. – ChrisMM Feb 04 '20 at 12:15
  • I have suggested to disable this warning only for **msvc**. He can still get all the warnings using **gcc**. Each compiler has different policies for warning reporting. msvc is strict for certain type of warnings then gcc. You can disable that warning knowing that msvc's suggestion is not required. For me it is more beneficial to enable all warnings in all compilers and then disable some of them for that particular compiler that are too strict or falsely reported. – Naveed Rasheed Feb 04 '20 at 12:27
  • @ChrisMM what are failing to understand that there is no such thing as a **panacea**. Every solution serves in some particular case. You need to see the accepted answer where it is also encouraged to disable this warning. – Naveed Rasheed Feb 04 '20 at 12:38
  • Disabling a warning after turning on all warnings defeats the purpose of turning on all the warnings. I'm not sure what is complicated about this. I understand you are saying "in MSVC only", but what if that is the main development environment? Maybe OP was moving away from gcc? OP specifically stated _Just disable it globally for all files? It does not seems to be good idea for me_, which clearly states that your proposed solution is **not** good for them. And, as you mentioned, other answers already suggest disabling, so at that point, what does your answer add? – ChrisMM Feb 04 '20 at 12:50
  • If a compiler can report 100 categories of warnings and you just want to disable 1 of them and enable other 99 categories, how would you do that? To answer your question, my answer adds how you can disable this warning using compiler switch at project level in msvc. – Naveed Rasheed Feb 04 '20 at 13:03