简体   繁体   English

C/C++:如何使用 do-while(0); 没有像 C4127 这样的编译器警告的构造?

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

I'm often use do-while(0) construct in my #defines, for the reasons described in this answer .由于这个答案中描述的原因,我经常在#defines 中使用 do-while(0) 构造。 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.所以我通常将-Wall与 gcc 一起使用,将/Wall与 MSVC 一起使用。

Unfortunately MSVC complain about do-while(0) construct:不幸的是 MSVC 抱怨 do-while(0) 构造:

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.这对我来说似乎不是个好主意。

Summary: This warning (C4127) in this particular case is a subtle compiler bug.摘要:此特定情况下的此警告 (C4127) 是一个微妙的编译器错误。 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.它旨在捕捉逻辑表达式在不明显的情况下计算为常量的情况(例如, if(a==a && a!=a) ,不知何故,它把while(true)和其他有用的构造变成了无效的.

Microsoft recommends using for(;;) for infinite loop if you want to have this warning on, and there is no solution for your case.如果您想打开此警告,Microsoft 建议使用for(;;)进行无限循环,并且没有针对您的情况的解决方案。 This is one of very few Level-4 warnings my company's development conventions allow to disable.这是我公司的开发约定允许禁用的极少数 4 级警告之一。

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 :正如Michael BurrCarl Smotricz回答中指出的那样,对于 Visual Studio 2008+,您可以使用__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.如果您希望宏不可读,您可以将它放在一行上(没有\\ s)。

I have a pattern that I based off an answer here & it works on clang, gcc & MSVC.我有一个基于这里答案的模式,它适用于 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:这也适用于上面指出的情况,如果 F 在函数中使用“ONCE”:

#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:编辑:多年后,我意识到我忘记添加我实际为此宏编写的模式 - “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;

This gives you a try/finally-ish construct that's more structured than a crufty goto to your cleanup & return code.这为您提供了一个 try/finally-ish 结构,它比清理和返回代码的笨拙转到更有条理。 Using this ONCE macro instead of while(0) shuts VS up.使用这个 ONCE 宏而不是 while(0) 会关闭 VS。

Using newer versions of the MS compiler, you can use warning suppression:使用较新版本的 MS 编译器,您可以使用警告抑制:

#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.您也可以推送/禁用/弹出,但抑制是一种更方便的机制。

This compiler bug was fixed in Visual Studio 2015 Update 1, even if the releases notes don't mention it.这个编译器错误在 Visual Studio 2015 Update 1 中得到修复,即使发行说明没有提到它。

The bug was explained in one of the previous answers though:该错误在之前的一个答案中得到了解释:

Summary: This warning (C4127) in this particular case is a subtle compiler bug.摘要:此特定情况下的此警告 (C4127) 是一个微妙的编译器错误。 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.它旨在捕捉逻辑表达式在不明显的情况下计算为常量的情况(例如,if(a==a && a!=a),并且不知何故,它将 while(true) 和其他有用的构造变成无效的.

Here's another possible approach, which avoids C4127, C4548 and C6319 (VS2013 code analysis warning), and doesn't require macros or pragmas:这是另一种可能的方法,它避免了 C4127、C4548 和 C6319(VS2013 代码分析警告),并且不需要宏或编译指示:

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.这优化了,并且在 GCC 4.9.2 和 VS2013 中编译时没有警告。 In practice it could go in a namespace.在实践中,它可以进入命名空间。

The warning is due to the while(false) .警告是由于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.只需在 BEGIN 和 END 之间插入您的代码。

You can use你可以使用

do {
    // Anything you like
} WHILE_FALSE;

And earlier define WHILE_FALSE macro as follows:之前定义WHILE_FALSE宏如下:

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

Verified on MSVC++2013.在 MSVC++2013 上验证。

You can use comma operator instead of do-while(0) construct for multi-statement macro to be used in expressions.对于要在表达式中使用的多语句宏,您可以使用逗号运算符代替 do-while(0) 构造。 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.请注意,在包含宏(第二个 FOO)的逗号中,最后一条语句(Statement3)的结果将是整个宏的结果。

This "while(0)" stuff is a hack and has just turned around to bite you.这个“while(0)”的东西是一个黑客,刚刚转过身来咬你。

Does your compiler offer #pragma s for selectively and locally turning off specific error messages?您的编译器是否提供#pragma s 以选择性地和本地关闭特定的错误消息? 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 (bool b = true; b;) do {f(); g(); b = false;} while (b) ? #define STUFF for (bool b = true; b;) do {f(); g(); b = false;} while (b) ?

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

I must say, I've never bothered with the do..while construct in macros.我必须说,我从来没有为宏中的 do..while 结构而烦恼。 All code in my macros is itself included in braces, but without the do-..while.我的宏中的所有代码本身都包含在大括号中,但没有 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:您可以使用 #pragma 警告来:

  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) (您需要在编译指示之前使用 #,但 SO 很难同时处理它们并进行格式化)

#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.不要在 while 条件中使用显式值。

You can make it like this:你可以这样做:

file1.h文件1.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.变量 I_am_a_zero 应该在一些 .c 文件中定义。

Anyway this warning doesn't show up in GCC :)无论如何,此警告不会出现在 GCC 中 :)

See this related question .请参阅此相关问题

Well, for me, the following works without the C4127 warning:好吧,对我来说,以下工作没有 C4127 警告:

#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当然,编译器很聪明,zzsome 不应该是一个常量

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

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.还没有检查它是否被编译器优化掉了,但我猜是。

You could use for loop as:您可以将for循环用作:

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

Macro:宏:

#define BEGIN \
  for (;;) {

#define END \
  break; }

I'd use我会用

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

}

This is equivalent to这相当于

do
{
}while(0);

and yields no warnings.并且不产生任何警告。

请使用编译器开关/wd"4127"在您的项目中禁用此警告。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM