124

これはスタイルの問題かもしれませんが、私たちの開発チームには少し意見が分かれており、他の誰かがこの問題についてアイデアを持っているかどうか疑問に思っていました...

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

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

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

4

20 に答える 20

88

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

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

  DoSomethingSlowWithTimeout(DEBUG_ENABLED? 5000 : 1000);

... それ以外の ...

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

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

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

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

#define DEBUG_ENABLED true

それ以外の

#define DEBUG_ENABLED (1)
于 2008-09-25T19:28:50.547 に答える
58

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

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

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

于 2010-07-03T17:57:55.480 に答える
38

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

#define FOO 0

それから

#ifdef FOO // is true
#if FOO // is false, because it evaluates to "#if 0"
于 2008-09-25T21:00:29.250 に答える
23

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

未定義の関数マクロが使用されているため、プリプロセッサでエラーが発生した可能性があります。

于 2010-07-01T18:32:52.913 に答える
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 を使用する傾向があります。

于 2008-09-25T20:48:58.717 に答える
15

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

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

于 2008-09-25T18:34:23.690 に答える
8

私自身が好む:

#if defined(DEBUG_ENABLED)

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

#if !defined(DEBUG_ENABLED)

対。

#ifndef(DEBUG_ENABLED)
于 2008-09-25T18:30:47.193 に答える
7

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

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

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

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

「expression has no effect」という警告が表示され、それを取り除きたい場合は、次の方法があります。

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

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

debug_print("i=%d\n", i);
于 2008-09-25T19:55:51.480 に答える
5

#ifスイッチがそこにあることを検出しながら、0 に設定して機能をオフにするオプションを提供します。
個人的には常に#define DEBUG 1#if または #ifdef のいずれかでキャッチできるようにしています

于 2008-09-25T18:29:25.590 に答える
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 defined) を使用し、そして何よりも、それらに非常に長い名前を付けることを意味します。これは「たまたま」であり、正規の C++ コードに影響を与えることは決してありません。

ポスト スクリプトム

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

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

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

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

面白い。:-)

于 2008-09-25T19:00:45.467 に答える
2

最初のものは私にはより明確に思えます。定義済み/未定義と比較して、フラグにする方が自然なようです。

于 2008-09-25T18:31:30.233 に答える
2

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

于 2008-09-25T18:38:30.543 に答える
1

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

于 2008-09-25T19:04:15.973 に答える
1

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

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

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

于 2017-08-04T16:14:42.977 に答える
0

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

以下は、Stephen C. Dewhurst によるC++ Gotchasで #if の使用について述べられている内容です。

于 2008-09-25T18:32:53.270 に答える
0

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

于 2008-09-25T18:34:18.557 に答える