web-dev-qa-db-ja.com

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

これはスタイルの問題かもしれませんが、開発チームには少し意見の相違があり、他の誰かがこの問題について何かアイデアを持っているのではないかと思いました...

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

//---- 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
}

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

108
Jon Cage

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

最初に、プリプロセッサでDEBUG_ENABLEDを使用できますおよびコンパイル済みテスト。例-多くの場合、デバッグを有効にするとタイムアウトが長くなるので、#ifを使用して、次のように記述できます。

  DoSomethingSlowWithTimeout(DEBUG_ENABLED? 5000 : 1000);

... の代わりに ...

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

第二に、#defineからグローバル定数に移行する場合は、より良い位置にいます。 #definesは通常、ほとんどのC++プログラマーに眉をひそめられます。

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

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

#define DEBUG_ENABLED true

の代わりに

#define DEBUG_ENABLED (1)
78
Roddy

彼らは両方とも恐ろしいです。代わりに、これを行います:

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

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

55
R..

#ifdefは、与えられたトークンが定義されているかどうかをチェックするだけです。

#define FOO 0

それから

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

複数のファイルで同じ問題が発生しており、「機能フラグ」ファイルを含めることを忘れているという問題が常にあります(コードベースが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

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

18
Brent Priddy

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

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

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

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

15
Mike Thompson

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

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

14
Derek Park

私自身が好む:

#if defined(DEBUG_ENABLED)

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

#if !defined(DEBUG_ENABLED)

vs.

#ifndef(DEBUG_ENABLED)
7
Jim Buck

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

#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);
6
Lev

#ifは、スイッチが存在することを検出しながら、機能をオフにするために0に設定するオプションを提供します。
個人的に私は常に#define DEBUG 1ので、#ifまたは#ifdefでキャッチできます

4
Martin Beckett

#ifおよび#define MY_MACRO(0)

#ifを使用すると、「定義」マクロ、つまり、コード内で「(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 ===|

oneエラーのみ。

これは、マクロが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|

面白い。 :-)

3
paercebal

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

2
David Nehme

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

#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
tmanthey

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

2
axblount

どちらもまったく同じです。慣用的な使用では、#ifdefは定義されているかどうかを確認するためだけに使用されます(そして、あなたの例で使用するもの)、#ifは#if defined(A)&&!.

2
zvrba

以前は#ifdefを使用していましたが、ドキュメント化のためにDoxygenに切り替えたときに、コメントアウトされたマクロをドキュメント化できないことがわかりました(または、少なくともDoxygenは警告を生成します)。これは、現在有効になっていない機能スイッチマクロをドキュメント化できないことを意味します。

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

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

1
StefanB

複数レベルのデバッグが必要な場合は、#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

これらすべてのログ行を他のことをデバッグする方法を使用せずに、メモリリークのデバッグを簡単にします。

#ifndef定義を囲むと、コマンドラインで特定のデバッグレベルを選択しやすくなります。

make -DDEBUG_LEVEL=2
cmake -DDEBUG_LEVEL=2
etc

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

0
memtha

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

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

出力:

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

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

0
Orient

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

これは、Stephen C. Dewhurstによる C++ Gotchas が#ifの使用について述べていることです。

0
Dima

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

0
tloach