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

13,084

Solution 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.

Solution 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)

Solution 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.

Solution 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.

Solution 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.

Share:
13,084

Related videos on Youtube

bialix
Author by

bialix

Just a programmer. C/C++, Python. Author of IntelHex Python library.

Updated on February 03, 2020

Comments

  • bialix
    bialix about 4 years

    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.

    • Thomas Matthews
      Thomas Matthews over 14 years
      Since you've added the C++ tag, is it possible convert the #define macros into inline functions? This would be safer.
    • bialix
      bialix over 14 years
      I've also added C tag, so I've thought I've asked about C-compatible solution. Should I remove C++ tag then?
    • Tony Delroy
      Tony Delroy about 13 years
      Have you tried a condition like sizeof(char) != 1...?
    • Mooing Duck
      Mooing Duck almost 8 years
      @bialix: Since the langauages and solutions are vastly different, yes. Remove the language tag you dont want answers for
  • bialix
    bialix over 14 years
    Yes, MSVC supports pragmas, but how can I wrap macros itself in that pragma?
  • bialix
    bialix over 14 years
    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.
  • Carl Smotricz
    Carl Smotricz over 14 years
    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.
  • bialix
    bialix over 14 years
    do-while(0) is wide used construct AFAIK.
  • Admin
    Admin over 14 years
    I know. I'm suggesting it is not necessary.
  • Prasanth Kumar
    Prasanth Kumar over 14 years
    +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.
  • josesuero
    josesuero over 14 years
    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
  • Johannes Schaub - litb
    Johannes Schaub - litb over 14 years
    I sometimes forget semicolons, and it makes the code horrible to read :/
  • Admin
    Admin over 14 years
    @litb Who are you talking to? If me, you can either use or omit the semicolon. And which bit of the code looks "horrible"?
  • Admin
    Admin over 14 years
    @Tomas See my second paragraph.
  • bialix
    bialix over 14 years
    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
  • Phil Miller
    Phil Miller over 14 years
    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).
  • ephemient
    ephemient over 14 years
    I hardly expect anybody using MSVC to be compiling plain C.
  • TofuBeer
    TofuBeer over 14 years
    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 :-)
  • CodingLab
    CodingLab over 14 years
    @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.
  • bialix
    bialix over 14 years
    Interesting, but actually does not help. It produces another warning :-) warning C4548: expression before comma has no effect; expected expression with side-effect
  • bialix
    bialix over 14 years
    Is not this const variable will prevent for optimization of the loop and will introduce unneeded check for zero?
  • bialix
    bialix over 14 years
    OK, so I'm that crazy guy then. BTW, C extensions for Python usually compiled with MSVC on Windows.
  • jamesdlin
    jamesdlin over 14 years
    Yes, that's why Yousf said it will add more cycles to your code.
  • Graeme Perrow
    Graeme Perrow over 14 years
    You can't put pragmas in the macro, so you'd have to disable the warning everywhere you use the macro. Nasty.
  • Yousf
    Yousf over 14 years
    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.
  • Pavel Radzivilovsky
    Pavel Radzivilovsky over 14 years
    to me, for(;;) just looks ugly.
  • Michael Burr
    Michael Burr almost 14 years
    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): msdn.microsoft.com/en-us/library/d9x1s805.aspx
  • Alexandre C.
    Alexandre C. over 13 years
    Disabled at my company too (on a per-header basis actually). This thrashes the build output when using boost.
  • Pavel Radzivilovsky
    Pavel Radzivilovsky over 13 years
    @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.
  • Alexandre C.
    Alexandre C. over 13 years
    This is what we do, but we manually put the pragmas before and after the boost headers.
  • Markus Joschko
    Markus Joschko about 13 years
    Bialix: Expand your macro to a call to an inline function. Put the do-while in this function and wrap it with pragma.
  • sharptooth
    sharptooth over 12 years
    @bialix: Can be easily fixed like this: do {} while( (void)0, 0)
  • Adrian McCarthy
    Adrian McCarthy over 11 years
    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
    Adrian McCarthy over 11 years
    +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.
  • Michel de Ruiter
    Michel de Ruiter almost 11 years
    Shouldn't there be a semicolon after stuff()?
  • Joel Falcou
    Joel Falcou over 10 years
    This is one nice way of putting it.
  • Motti
    Motti over 10 years
    You missed the point, the do { } while(0) note the lack of semi colon, is used in macros for a reason
  • supercat
    supercat over 10 years
    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.
  • Tom Whittock
    Tom Whittock over 9 years
    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.
  • Matthieu
    Matthieu almost 9 years
    The second version also generates a C6319 warning in code analysis VC2012+.
  • bolov
    bolov about 8 years
    it is optimized away.
  • bolov
    bolov about 8 years
    and you can get rid of one set of parenthesis: } while ([]() { return 0; }());
  • mtb
    mtb almost 8 years
    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.
  • Slava
    Slava almost 8 years
    More convenient mechanism for bug workaround, rather than fixing bug. Nice job MS!
  • ChrisMM
    ChrisMM about 4 years
    OP specifically turned on all warnings, so why suggest disabling? That would defeat the purpose of enabling the warning in the first place.
  • ChrisMM
    ChrisMM about 4 years
    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.
  • Naveed Rasheed
    Naveed Rasheed about 4 years
    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
    Naveed Rasheed about 4 years
    @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.
  • ChrisMM
    ChrisMM about 4 years
    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?
  • Naveed Rasheed
    Naveed Rasheed about 4 years
    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.