15

プログラムのデバッグ目的で冗長性を高めることができるようにしたいと考えています。もちろん、実行時にスイッチ/フラグを使用してそれを行うことができます。しかし、コードにすべての「if」ステートメントを追加する必要があるため、これは非常に非効率的です。

したがって、コンパイル中に使用するフラグを追加して、必要のないときにプログラムのパフォーマンス/サイズに影響を与えることなく、オプションの、通常は遅いデバッグ操作をコードに含めることができます。例を次に示します。

/* code */
#ifdef _DEBUG_
/* do debug operations here 
#endif

したがって、 -D_DEBUG_ を指定してコンパイルするとうまくいくはずです。それがなければ、その部分は私のプログラムに含まれません。

別のオプション (少なくとも i/o 操作の場合) は、少なくとも次のような i/o 関数を定義することです。

#ifdef _DEBUG_
#define LOG(x) std::clog << x << std::endl;
#else
#define LOG(x) 
#endif

ただし、これはおそらく最もクリーンな方法ではないと強く思います。それで、代わりに何をしますか?

4

10 に答える 10

19

が定義されていない#ifdef場合、関数の本体が空になるように、実際の関数で使用することを好みます。_DEBUG_

void log(std::string x)
{
#ifdef _DEBUG_
  std::cout << x << std::endl;
#endif
}

この選択には、次の 3 つの大きな理由があります。

  1. が定義されていない場合_DEBUG_、関数定義は空であり、最新のコンパイラはその関数への呼び出しを完全に最適化します (もちろん、定義はその翻訳単位内に表示されるはずです)。
  2. #ifdefガードは、 を呼び出すたびに適用するのではなく、コードの小さなローカライズされた領域にのみ適用する必要がありますlog
  3. 多くのマクロを使用する必要がないため、コードの汚染を回避できます。
于 2013-02-11T13:22:26.637 に答える
2

マクロを使用して関数の実装を変更できます (sftrabbit のソリューションのように)。そうすれば、コードに空の場所が残されることはなく、コンパイラは「空の」呼び出しを最適化します。

デバッグとリリースの実装に 2 つの異なるファイルを使用して、IDE/ビルド スクリプトに適切なファイルを選択させることもできます。これにはまったく関係ありません#defines。DRY ルールを覚えて、クリーンなコードをデバッグ シナリオで再利用できるようにします。

彼は実際にあなたが直面している実際の問題に大きく依存していると思います。いくつかの問題は 2 番目の解決策の恩恵を受けますが、単純なコードは単純な定義の方が優れている場合があります。

于 2013-02-11T13:20:06.460 に答える
2

説明する両方のスニペットは、条件付きコンパイルを使用して、コンパイル時のスイッチを介してデバッグを有効または無効にする正しい方法です。ただし、実行時にデバッグフラグをチェックすることは「コードに追加する必要があるすべての「if」ステートメントのために非常に非効率になる可能性がある」という主張はほとんど正しくありません。ほとんどの実際のケースでは、実行時チェックは速度に影響しませんしたがって、実行時フラグを保持することで潜在的な利点が得られる場合 (たとえば、デバッグをオンにして、再コンパイルせずに本番環境の問題を診断する場合)、代わりに実行時フラグを使用する必要があります。

于 2013-02-11T13:22:54.003 に答える
1

商用ソフトウェアの場合、実行時に顧客のサイトで利用できるデバッグ出力があると、通常は価値があります。すべてを最終的なバイナリにコンパイルする必要があると言っているわけではありませんが、顧客が予期しないことをコードに対して行うことはまったく珍しいことではありません [または、コードが予期しない方法で動作する原因となります] ]。顧客に「まあ、myprog -v 2 -l logfile.txtあなたがいつものことをしているなら、私にメールしてくださいlogfile.txt」と言うことができることは、非常に非常に便利なことです.

「ログを記録するかどうかを決定する if ステートメント」がペルーの最も深く暗いジャングルにない限り、つまり、タイトでパフォーマンス クリティカルなループの最も深いネスティング レベルにない限り、問題になることはめったにありません。入れておきます。

そのため、私は個人的に「常にそこにあり、常に有効であるとは限らない」アプローチを採用する傾向があります。それは、タイトなループの途中で余分なログを追加していることに気付かないということではありません。後でバグが修正されたときに削除するためだけです。

于 2013-02-11T13:34:29.710 に答える
1

追加のチェックについては、必要なことを正確に実行する assert ( assert.hを参照) に依存します。デバッグでコンパイルするときはチェックし、リリース用にコンパイルするときはチェックしません。

冗長性のために、あなたが提案するもののより多くのC++バージョンは、テンプレートパラメータとしてブール値を持つ単純な Logger クラスを使用します。しかし、マクロは Logger クラス内に保持されていれば問題ありません。

于 2013-02-11T13:25:57.277 に答える
0

私にとっては、アプリケーションごとに異なります。

常にログに記録したいアプリケーションがありました (たとえば、エラーが発生した場合、クライアントがアプリケーションのすべてのログを取得し、診断のために送信するアプリケーションがありました)。このような場合、ロギング API はおそらく関数 (つまり、マクロではない) に基づいており、常に定義されている必要があります。

ロギングが常に必要なわけではない場合や、パフォーマンスやその他の理由でロギングを完全に無効にする必要がある場合は、ロギング マクロを定義できます。

その場合、次のような単一行のマクロを好みます。

#ifdef NDEBUG
#define LOGSTREAM /##/
#else
#define LOGSTREAM std::clog
// or
// #define LOGSTREAM std::ofstream("output.log", std::ios::out|std::ios::app)
#endif

クライアントコード:

LOG << "Initializing chipmunk feeding module ...\n";
//...
LOG << "Shutting down chipmunk feeding module ...\n";
于 2013-02-11T13:38:17.283 に答える
0

他の機能と同じです。

私の仮定:

  • グローバル変数なし
  • インターフェイス用に設計されたシステム

詳細な出力が必要な場合は、2 つの実装を作成します。1 つはサイレント、もう 1 つは詳細です。アプリケーションの初期化時に、必要な実装を選択します。

たとえば、ロガー、ウィジェット、またはメモリ マネージャーなどです。

明らかに、コードを複製したくないので、必要な最小限のバリエーションを抽出します。戦略パターンまたはデコレータ パターンが何であるかを知っている場合、これらは正しい方向です。オープンクローズの原則に従います。

于 2013-02-11T13:56:09.763 に答える
0

プロジェクトで使用するコードの例。このようにして、可変引数リストを使用できます。DEBUGフラグが設定されていない場合、関連するコードは消去されます。

#ifdef DEBUG
#define PR_DEBUG(fmt, ...) \
    PR_DEBUG(fmt, ...) printf("[DBG] %s: " fmt, __func__, ## __VA_ARGS__)
#else
#define PR_DEBUG(fmt, ...)
#endif

使用法:

#define DEBUG
<..>
ret = do_smth();
PR_DEBUG("some kind of code returned %d", ret);

出力:

[DBG] some_func: some kind of code returned 0

もちろん、printf()使用する任意の出力関数に置き換えることができます。さらに、タイムスタンプなどの追加情報が自動的に追加されるように、簡単に変更できます。

于 2013-02-11T13:24:26.037 に答える
0

条件付きコンパイルを行うときに、関数のようなマクロを回避できます。ロギングを行う通常の関数またはテンプレート関数を定義し、次の内部で呼び出すだけです。

#ifdef _DEBUG_
/* ... */
#endif

コードの一部。

于 2013-02-11T13:22:18.987 に答える
0

少なくとも *Nix ユニバースでは、この種のデフォルトの定義は( no-debugNDEBUGを読む) です。定義されている場合、コードはデバッグ コードをスキップする必要があります。つまり、次のようにします。

#ifdef NDEBUG
inline void log(...) {}
#else
inline void log(...) { .... }
#endif
于 2013-02-11T13:26:43.300 に答える