web-dev-qa-db-ja.com

C / C ++:do-while(0)の使用方法。 C4127のようなコンパイラ警告なしで構築しますか?

説明されている理由のために、#definesでdo-while(0)構文をよく使用します この回答では 。また、コンパイラからの可能な限り高い警告レベルを使用して、より潜在的な問題をキャッチし、コードをより堅牢でクロスプラットフォームにするようにしています。したがって、私は通常、gccで-Wallを使用し、MSVCで/Wallを使用しています。

残念ながら、MSVCはdo-while(0)構文について文句を言います。

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

この警告についてどうすればよいですか?

すべてのファイルに対してグローバルに無効にするだけですか?それは私には良い考えではないようです。

44
bialix

概要:この特定の場合のこの警告(C4127)は、コンパイラーの微妙なバグです。自由に無効にしてください。

詳細:

これは、論理式が非自明な状況(if(a==a && a!=a)など)で定数に評価される状況をキャッチすることを目的としており、どういうわけか、while(true)やその他の有用な構造が無効になりました。

この警告をオンにしたい場合は、無限ループにfor(;;)を使用することをお勧めしますが、その場合の解決策はありません。これは、私の会社の開発規則で無効にできる数少ないレベル4の警告の1つです。

46

おそらくあなたのコードは より多くのフクロウ を必要とします:

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

または、フォトジェニックではありませんが、警告の生成も少なくなります。

do { stuff(); } while ((void)0,0)
28
Eric Seppanen

Michael Burr に記載されているように Carl Smotricz ' answer 、Visual Studio 2008+の場合は __ pluginma を使用できます。

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

マクロを読み取れないようにしたい場合は、1行に(\sなしで)配置できます。

18

ここでの回答に基づいたパターンがあり、clang、gcc、MSVCで機能します。他の人にも役立つことを願って、ここに投稿します。ここでの回答が私がそれを作成するのに役立ったからです。

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

そして私はそれを次のように使用します:

do {
   // Some stuff
} ONCE;

これはマクロでも使用できます。

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

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

これは、Fが関数で「ONCE」を使用する場合、上記で指摘した場合にも機能します。

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

編集:数年後、私が実際にこのマクロを書いたパターンを追加するのを忘れたことに気づきました-「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;

これにより、クリーンアップとリターンコードへの無愛想なgotoよりも構造化されたtry/finallyっぽい構造が得られます。 while(0)の代わりにこのONCEマクロを使用すると、VSがシャットダウンします。

15
nevelis

新しいバージョンのMSコンパイラを使用すると、警告抑制を使用できます。

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

プッシュ/無効化/ポップすることもできますが、抑制ははるかに便利なメカニズムです。

4
Seth Moore

C4127、C4548、およびC6319(VS2013コード分析警告)を回避し、マクロまたはプラグマを必要としない、別の可能なアプローチを次に示します。

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

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

これは最適化され、GCC4.9.2およびVS2013では警告なしにコンパイルされます。実際には、名前空間に入れることができます。

3
james

このコンパイラのバグは、 リリースノート に言及していなくても、Visual Studio 2015 Update1で修正されました。

ただし、バグは以前の回答の1つで説明されています。

概要:この特定の場合のこの警告(C4127)は、コンパイラーの微妙なバグです。自由に無効にしてください。

これは、論理式が非自明な状況(if(a == a && a!= a)など)で定数に評価される状況をキャッチすることを目的としており、どういうわけか、while(true)やその他の有用な構造が無効になりました。

3
chris.briend

使用できます

do {
    // Anything you like
} WHILE_FALSE;

そして、以前はWHILE_FALSEマクロを次のように定義しました。

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

MSVC++ 2013で検証済み。

2
Serge Rogatch

警告はwhile(false)によるものです。この site は、この問題を回避する方法の例を示しています。サイトからの例(コード用に作り直す必要があります):

#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  

BEGINとENDの間にコードを挿入するだけです。

2
user195488

この「while(0)」のものはハックであり、あなたを噛むために振り返ったところです。

コンパイラは、特定のエラーメッセージを選択的かつローカルにオフにするための#pragmasを提供していますか?もしそうなら、それは賢明な選択肢かもしれません。

1
Carl Smotricz

式で使用されるマルチステートメントマクロには、do-while(0)構文の代わりにコンマ演算子を使用できます。したがって、代わりに:

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

使用する:

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

これはプラットフォームとは独立して機能し、コンパイラの警告を回避できます(最高の警告レベルが選択されている場合でも)。マクロ(2番目のFOO)を含むコンマでは、最後のステートメント(Statement3)の結果がマクロ全体の結果になることに注意してください。

1
Armen Anoyan

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

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

1
ephemient

これにより警告が無効になり、コンパイラは引き続きコードを最適化できます。

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
0
wonder.mice

まあ、私にとって、以下はC4127警告なしで動作します:

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

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

もちろん、コンパイラは賢く、zzsomeは定数であってはなりません

0
Deyan Ivanov

解決策はありますが、コードにサイクルが追加されます。 while条件で明示的な値を使用しないでください。

あなたはそれをこのようにすることができます:

file1.h

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

変数I_am_a_zeroは、いくつかの.cファイルで定義する必要があります。

とにかく、この警告はGCCには表示されません:)

これを参照してください 関連する質問

0
Yousf

#pragma警告を使用して次のことができます。

  1. 状態を保存します
  2. 警告を無効にする
  3. 問題のあるコードを書く
  4. 警告を以前の状態に戻します

(プラグマの前に#が必要ですが、SOはプラグマの処理とフォーマットを同時に行うのに苦労しています)

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

警告をオン/オフにするために選択される可能性のあるコマンドライン引数に干渉したくないため、無効/有効にするのではなく、警告をプッシュ/ポップする必要があります(誰かがコマンドラインを使用して警告をオフにする可能性があります。強制的にオンに戻したくない...上記のコードはそれを処理します)。

これは、必要な部分だけを制御できるため、警告をグローバルにオフにするよりも優れています。また、マクロの一部にすることもできます。

0
TofuBeer

これが最短バージョンであることがわかりました

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

コンパイラによって最適化されているかどうかは確認していませんが、最適化されていると思います。

0
Niklas R

forループは次のように使用できます。

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

大きい:

#define BEGIN \
  for (;;) {

#define END \
  break; }
0
wolfram77

言わなければならないのは、マクロでdo..whileを構築することを気にしたことは一度もないということです。私のマクロのすべてのコードはそれ自体が中括弧に含まれていますが、do-.. whileは含まれていません。例えば:

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

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

また、私自身のコーディング標準(および何年にもわたる非公式な慣行)は、すべてのブロックを中括弧で囲むようにすることでした。これにより、多かれ少なかれ問題が解消されます。

0
anon