#ifdefと#if-コードの特定のセクションのコンパイルを有効/無効にする方法として、どちらが優れている/より安全ですか?


112

これはスタイルの問題かもしれませんが、私たちの開発チームには少し分裂があるので、他の誰かがこの問題について何か考えを持っているのではないかと思いました...

基本的に、通常の開発中にオフにするデバッグ印刷ステートメントがいくつかあります。個人的に私は以下を行うことを好みます:

//---- SomeSourceFile.cpp ----

#define DEBUG_ENABLED (0)

...

SomeFunction()
{
    int someVariable = 5;

#if(DEBUG_ENABLED)
    printf("Debugging: someVariable == %d", someVariable);
#endif
}

しかし、チームの中には次のものを好む人もいます:

// #define DEBUG_ENABLED

...

SomeFunction()
{
    int someVariable = 5;

#ifdef DEBUG_ENABLED
    printf("Debugging: someVariable == %d", someVariable);
#endif
}

...これらの方法のどれがあなたにとってより良く聞こえますか、そしてなぜですか?私は、何かが常に定義されており、他の定義を破壊する危険がないため、最初の方が安全だと感じています。


注:#if使用#elifすると、とは異なり、一貫した方法で使用することもできます#ifdef。したがって、単にを#define BLAH使用する代わりに、#define BLAH 1と一緒#if BLAHに使用するなど
Andrew

回答:


82

私の最初の反応は#ifdefもちろんでしたが#if実際にはこれにはいくつかの重要な利点があると思います-これが理由です:

まず、DEBUG_ENABLEDプリプロセッサコンパイルされたテストで使用できます。例-多くの場合、デバッグが有効になっているときにタイムアウトを長くしたいので#if、を使用してこれを書くことができます

  DoSomethingSlowWithTimeout(DEBUG_ENABLED? 5000 : 1000);

... の代わりに ...

#ifdef DEBUG_MODE
  DoSomethingSlowWithTimeout(5000);
#else
  DoSomethingSlowWithTimeout(1000);
#endif

次に、a #defineからグローバル定数に移行したい場合は、より良い位置にいます。#definesは通常、ほとんどのC ++プログラマーに嫌われています。

そして、第三に、あなたはあなたのチームに分裂があると言います。私の推測では、これは異なるメンバーがすでに異なるアプローチを採用しており、標準化する必要があることを意味します。#if優先される選択である裁定は、がfalseの#ifdef場合でも、使用するコードがコンパイルされて実行されることを意味しますDEBUG_ENABLED。また、本来あるべきでないときに生成されるデバッグ出力を追跡して削除する方がはるかに簡単です。

ああ、そして小さな読みやすさのポイント。で0/1ではなくtrue / falseを使用できるようにする必要があります#define。値は単一の字句トークンであるため、括弧は不要です。

#define DEBUG_ENABLED true

の代わりに

#define DEBUG_ENABLED (1)

この定数はデバッグの有効化/無効化に使用されない可能性があるため、#defineを0に設定して#ifdefをトリガーしてもそれほど害はありません。true / falseについては、C99で追加され、C89 / C90には存在しません。
マイケルカーマン

Micheal:彼/彼女は#ifdefの使用に反対していました?!
Jon Cage

7
ええ、問題の1つ#ifdefは、定義されていないものでも機能することです。それらが意図的に定義されているのか、タイプミスのために定義されているのか、あなたが何を持っているのか。
bames53 2013年

6
答えへのあなたの追加は間違っています。#if DEBUG_ENBALEDプリプロセッサによって検出されたエラーではありません。DEBUG_ENBALED定義されていない場合は0#ifディレクティブのトークンに展開されます。
R .. GitHub ICE HELPING ICEを停止する

6
@R ..多くのコンパイラでは、DEBUG_ENABLEDが定義されていないときに、「#if DEBUG_ENABLED」の警告を有効にできます。GCCでは「-Wundef」を使用します。Microsoft Visual Studioでは、「/ w14668」を使用して、レベル1の警告としてC4668をオンにします。
ウィル

56

彼らは両方とも恐ろしいです。代わりに、次のようにします。

#ifdef DEBUG
#define D(x) do { x } while(0)
#else
#define D(x) do { } while(0)
#endif

その後、デバッグコードが必要なときはいつでも、それをの中に置きD();ます。そして、あなたのプログラムはの恐ろしい迷路で汚染されていません#ifdef


6
@MatthieuM。実はオリジナル版で良かったと思います。セミコロンは空のステートメントとして解釈されます。ただし、セミコロンを忘れると危険になります。
Casey Kuball

31

#ifdef 指定されたトークンが定義されているかどうかを確認するだけです

#define FOO 0

その後

#ifdef FOO // is true
#if FOO // is false, because it evaluates to "#if 0"

20

これと同じ問題が複数のファイルにわたって発生しており、「機能フラグ」ファイルを含めるのを忘れている人には常に問題があります(コードベースが41,000ファイルを超えると、簡単に実行できます)。

feature.hがあった場合:

#ifndef FEATURE_H
#define FEATURE_H

// turn on cool new feature
#define COOL_FEATURE 1

#endif // FEATURE_H

しかし、その後、ヘッダーファイルをfile.cppに含めるのを忘れました。

#if COOL_FEATURE
    // definitely awesome stuff here...
#endif

次に、問題が発生します。この場合、コンパイラは未定義のCOOL_FEATUREを「false」と解釈し、コードを含めることができません。はいgccは未定義のマクロに対してエラーを引き起こすフラグをサポートします...しかし、ほとんどのサードパーティのコードは機能を定義するか定義しないので、これはそれほど移植性がありません。

このケースを修正するポータブルな方法と、機能の状態をテストする機能マクロを採用しました。

上記のfeature.hを次のように変更した場合:

#ifndef FEATURE_H
#define FEATURE_H

// turn on cool new feature
#define COOL_FEATURE() 1

#endif // FEATURE_H

しかし、ヘッダーファイルをfile.cppにインクルードするのを忘れました。

#if COOL_FEATURE()
    // definitely awseome stuff here...
#endif

未定義の関数マクロを使用しているため、プリプロセッサはエラーになります。


17

条件付きコンパイルを実行するために、#ifと#ifdefはほとんど同じですが、完全ではありません。条件付きコンパイルが2つのシンボルに依存している場合、#ifdefも機能しません。たとえば、PRO_VERSIONとTRIAL_VERSIONの2つの条件付きコンパイルシンボルがあるとすると、次のようになります。

#if defined(PRO_VERSION) && !defined(TRIAL_VERSION)
...
#else
...
#endif

上記の#ifdefを使用すると、特に#else部分を機能させることにより、はるかに複雑になります。

私は条件付きコンパイルを広範囲に使用するコードに取り組んでおり、#ifと#ifdefが混在しています。単純な場合には#ifdef /#ifndefを使用し、2つ以上のシンボルが評価されるときは常に#ifを使用する傾向があります。


1
#if definedあるものdefined、それは重要な単語ですか?
nmxprime 14

15

それは完全にスタイルの問題だと思います。どちらも、明らかに他のものよりも明らかな利点はありません。

一貫性はどちらの特定の選択よりも重要であるため、チームと集まり、1つのスタイルを選択し、それに固執することをお勧めします。


8

私自身が好きです:

#if defined(DEBUG_ENABLED)

反対の条件を探すコードを作成するのが簡単になるので、見つけやすくなります。

#if !defined(DEBUG_ENABLED)

#ifndef(DEBUG_ENABLED)

8
個人的には、その小さな感嘆符を逃す方が簡単だと思います!
Jon Cage

6
構文の強調表示を使って?:)構文の強調表示では、「ifndef」の「n」はすべて同じ色であるため、見つけるのがはるかに困難です。
ジムバック

#if definedと比較するときに#ifndefは#if!definedよりも見つけやすいということですが、すべての#if defined /#if!definedと#ifdef /#ifndefを比較すると、どちらも同じように読みにくくなります!
Jon Cage

@JonCageこのコメントから数年が経過していることは承知していますが#if ! defined!もっと目立ち、見逃しにくいように書いてもらえることを指摘したいと思います。
Pharap 2016年

@Pharap-それは確かに改善のように見えます:)
Jon Cage

7

それはスタイルの問題です。しかし、私はこれを行うより簡潔な方法をお勧めします:

#ifdef USE_DEBUG
#define debug_print printf
#else
#define debug_print
#endif

debug_print("i=%d\n", i);

これを一度実行したら、常にdebug_print()を使用して、印刷するか、何もしません。(はい、これはどちらの場合でもコンパイルされます。)このようにして、コードはプリプロセッサディレクティブで文字化けしません。

「式には効果がありません」という警告が表示され、それを取り除きたい場合は、次の方法があります。

void dummy(const char*, ...)
{}

#ifdef USE_DEBUG
#define debug_print printf
#else
#define debug_print dummy
#endif

debug_print("i=%d\n", i);

3
おそらく、印刷マクロは結局のところ最良の例ではありませんでした。実際には、より標準的なデバッグコードのコードベースで既にこれを行っています。私たちは..あなたは余分なデバッグをオンにするかもしれないエリアのための#if / #ifdefinedビットを使用
ジョン・ケージ

5

#ifスイッチが存在することを検出しながら、機能をオフにするためにそれを0に設定するオプションを提供します。
個人的にはいつも#define DEBUG 1#ifか#ifdefのどちらかで捕まえることができます


1
#define DEBUG = 0は#ifを実行せず、#ifdefを実行するため、これは失敗します
tloach

1
つまり、DEBUGを完全に削除することも、0に設定して無効にすることもできます。
マーティンベケット

それはあるはずです#define DEBUG 1。不可#define DEBUG=1
ケシャバGN 2013

4

#ifおよび#define MY_MACRO(0)

#ifを使用すると、「define」マクロ、つまり、コード内で検索されて「(0)」に置き換えられるマクロが作成されたことを意味します。これは、コードを潜在的なコード変更で汚染するため、C ++で見たくない「マクロ地獄」です。

例えば:

#define MY_MACRO (0)

int doSomething(int p_iValue)
{
   return p_iValue + 1 ;
}

int main(int argc, char **argv)
{
   int MY_MACRO = 25 ;
   doSomething(MY_MACRO) ;

   return 0;
}

g ++で次のエラーが発生します。

main.cpp|408|error: lvalue required as left operand of assignment|
||=== Build finished: 1 errors, 0 warnings ===|

1つのエラーのみ。

これは、マクロがC ++コードと正常に相互作用したことを意味します。関数の呼び出しは成功しました。この単純なケースでは、それはおかしいです。しかし、自分のコードで黙って遊んでいるマクロに関する私自身の経験は、喜びと充足感に満ちていないので...

#ifdefおよび#define MY_MACRO

#ifdefを使用すると、何かを「定義」することになります。あなたがそれに値を与えるということではありません。それはまだ汚染されていますが、少なくとも、「何も置き換えられない」ため、C ++コードでは妥当なコードステートメントと見なされません。上記と同じコードで、単純に定義すると、次のようになります。

#define MY_MACRO

int doSomething(int p_iValue)
{
   return p_iValue + 1 ;
}

int main(int argc, char **argv)
{
   int MY_MACRO = 25 ;
   doSomething(MY_MACRO) ;

   return 0;
}

次の警告を表示します。

main.cpp||In function int main(int, char**)’:|
main.cpp|406|error: expected unqualified-id before ‘=’ token|
main.cpp|399|error: too few arguments to function int doSomething(int)’|
main.cpp|407|error: at this point in file|
||=== Build finished: 3 errors, 0 warnings ===|

そう...

結論

私はコードにマクロなしで生きたいと思っていますが、複数の理由(ヘッダーガードの定義、またはマクロのデバッグ)のために、それはできません。

しかし、少なくとも、正当なC ++コードを使用してインタラクティブ性を最小限に抑えたいと思っています。つまり、値なしで#defineを使用し、#ifdefと#ifndef(またはJim Buckによって提案されたように#ifで定義されていても)を使用し、何よりも長い間名前を付けることで、右心の誰も使用しないそれは「偶然」であり、正当なC ++コードに影響を与えることは決してありません。

後記

さて、私の投稿を再読しているときに、自分の定義に追加するために正しいC ++になることのない値を見つけようとするべきではないかと思います。何かのようなもの

#define MY_MACRO @@@@@@@@@@@@@@@@@@

これは#ifdefおよび#ifndefで使用できますが、関数内で使用するとコードをコンパイルできません...私はこれをg ++で正常に試行し、エラーが発生しました:

main.cpp|410|error: stray ‘@’ in program|

面白い。:-)


マクロは危険である可能性があることには同意しますが、その最初の例はデバッグするのがかなり明白であり、もちろん1つのエラーしか発生しません。なぜもっと期待しますか?マクロの結果として多くの厄介なエラーが発生しました...
Jon Cage

確かに、あるソリューションと別のソリューションの違いはほとんど取るに足らないものです。しかし、この場合、2つの競合するコーディングスタイルについて話しているので、些細なことでさえ無視することはできません。それは、その後はすべて個人的な好みだからです(その時点では、正規化すべきではないと思います) )
paercebal 2008

4

それはスタイルの問題ではありません。また、問題は残念ながら間違っています。これらのプリプロセッサディレクティブをより良いまたはより安全な意味で比較することはできません。

#ifdef macro

「マクロが定義されている場合」または「マクロが存在する場合」を意味します。ここではマクロの値は関係ありません。それは何でもかまいません。

#if macro

常に値と比較する場合。上記の例では、これは標準の暗黙的な比較です。

#if macro !=0

#ifの使用例

#if CFLAG_EDITION == 0
    return EDITION_FREE;
#elif CFLAG_EDITION == 1
    return EDITION_BASIC;
#else
    return EDITION_PRO;
#endif

CFLAG_EDITIONの定義をコードに入れるか、

#define CFLAG_EDITION 1 

または、マクロをコンパイラフラグとして設定できます。こちらご覧ください


2

最初の方が私にははっきりしているようです。定義済み/未定義に比べて、フラグを作成する方が自然に見えます。


2

どちらもまったく同じです。慣用的な使用では、#ifdefは定義(および私があなたの例で使用するもの)をチェックするためだけに使用されますが、#ifは#if defined(A)&&!defined(B)などのより複雑な式で使用されます。


1
OPは、「#ifdef」と「#if defined」の間ではなく、「#ifdef /#if defined」と「#if」のどちらが良いかを求めていませんでした。
シャンク

1

少しOTですが、プリプロセッサでロギングをオン/オフにすることは、C ++では間違いなく最適ではありません。Apacheのlog4cxxのような優れたロギングツールがあり、オープンソースであり、アプリケーションの配布方法を制限しません。また、再コンパイルせずにログレベルを変更したり、ログをオフにした場合のオーバーヘッドが非常に少なくなったり、運用環境でログを完全にオフにしたりできるようになります。


1
私も同意し、実際にコードでそれを実行します。#ifなどを使用する可能性のあるものの例が必要でした
Jon Cage

1

以前はを使用#ifdefしていましたが、ドキュメンテーションをDoxygenに切り替えたときに、コメント化されたマクロをドキュメント化できない(または、少なくともDoxygenが警告を生成する)ことに気付きました。これは、現在有効になっていない機能切り替えマクロを文書化できないことを意味します。

Doxygenに対してのみマクロを定義することは可能ですが、これはコードの非アクティブな部分のマクロも文書化されることを意味します。私は個人的に機能スイッチを表示し、それ以外の場合は現在選択されているもののみを文書化したいと思います。さらに、Doxygenがファイルを処理するときにのみ定義する必要がある多くのマクロがある場合、コードはかなり乱雑になります。

したがって、この場合は常にマクロを定義してを使用することをお勧めします#if


0

私は常に#ifdefとコンパイラフラグを使用してそれを定義しています...


特別な理由(好奇心から)?
Jon Cage

2
正直言って、私はそれについて考えたことは一度もありません-私が働いた場所でそれがどのように行われたかだけです。これは、プロダクションビルドのコードを変更する代わりに、デバッグの場合は「make DEBUG」、通常の場合は「make PRODUCTION」であるという利点を提供します
tloach

0

あるいは、グローバル定数を宣言して、プリプロセッサー#ifの代わりにC ++ ifを使用することもできます。コンパイラーは未使用のブランチを最適化し、コードをクリーンにします。

ここでは何であるC ++落とし穴がスティーブン・C.デューハーストでの場合は#を使用する方法について述べています。


1
これはお粗末な解決策であり、次の問題があります。1。関数でのみ機能し、不要なクラス変数などを削除できません。2。コンパイラーが到達できないコードに関する警告をスローする可能性があります。すべてのデバッグ関数を定義しておく必要があります。など
Don Neufeld '25

最初の質問は、debug printfsに関するものでしたので、ここでは不要なクラス変数は問題になりません。次に、最新のコンパイラの機能を考えると、#ifdefsをできるだけ使用しないでください。ほとんどの場合、代わりにビルド構成またはテンプレート特殊化を使用できます。
Dima

0

ドライバーに条件付き定義を指定する方法が異なる場合は違いがあります。

diff <( echo | g++ -DA= -dM -E - ) <( echo | g++ -DA -dM -E - )

出力:

344c344
< #define A 
---
> #define A 1

つまり、これ-DAは同義語で-DA=1あり、値を省略した場合、#if A使用時に問題が発生する可能性があります。


0

#define DEBUG_ENABLED (0)複数のレベルのデバッグが必要な場合が好きです。例えば:

#define DEBUG_RELEASE (0)
#define DEBUG_ERROR (1)
#define DEBUG_WARN (2)
#define DEBUG_MEM (3)
#ifndef DEBUG_LEVEL
#define DEBUG_LEVEL (DEBUG_RELEASE)
#endif
//...

//now not only
#if (DEBUG_LEVEL)
//...
#endif

//but also
#if (DEBUG_LEVEL >= DEBUG_MEM)
LOG("malloc'd %d bytes at %s:%d\n", size, __FILE__, __LINE__);
#endif

他のものをデバッグするためにこれらのログ行をすべて使用しなくても、メモリリークのデバッグが容易になります。

また、#ifndefdefine の周りでは、コマンドラインで特定のデバッグレベルを簡単に選択できます。

make -DDEBUG_LEVEL=2
cmake -DDEBUG_LEVEL=2
etc

そうでない場合は#ifdef、コンパイラ/メイクフラグがファイル内のフラグによってオーバーライドされるため、私は利点を提供します。したがって、コミットを行う前にヘッダーを元に戻すことを心配する必要はありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.