次の疑似コードのように、DEBUGが定義されているときにデバッグメッセージの印刷に使用できるマクロを作成しようとしています。
#define DEBUG 1
#define debug_print(args ...) if (DEBUG) fprintf(stderr, args)
これはマクロでどのように行われますか?
次の疑似コードのように、DEBUGが定義されているときにデバッグメッセージの印刷に使用できるマクロを作成しようとしています。
#define DEBUG 1
#define debug_print(args ...) if (DEBUG) fprintf(stderr, args)
これはマクロでどのように行われますか?
回答:
#define debug_print(fmt, ...) \
do { if (DEBUG) fprintf(stderr, fmt, __VA_ARGS__); } while (0)
C99を使用していることを前提としています(可変引数リスト表記は以前のバージョンではサポートされていません)。do { ... } while (0)
コード文(関数呼び出し)のような役割を果たしイディオムを保証します。コードを無条件に使用すると、コンパイラーは常にデバッグコードが有効であることを確認しますが、オプティマイザはDEBUGが0の場合にコードを削除します。
#ifdef DEBUGを使用する場合は、テスト条件を変更します。
#ifdef DEBUG
#define DEBUG_TEST 1
#else
#define DEBUG_TEST 0
#endif
次に、DEBUGを使用した場所でDEBUG_TESTを使用します。
あなたはフォーマット文字列の文字列リテラル(おそらく良いアイデアとにかく)を主張した場合、あなたはまた、のようなものを導入することができ__FILE__
、__LINE__
および__func__
診断を向上させることができ、出力、中に:
#define debug_print(fmt, ...) \
do { if (DEBUG) fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
__LINE__, __func__, __VA_ARGS__); } while (0)
これは文字列の連結に依存して、プログラマーが書き込むよりも大きなフォーマット文字列を作成します。
C89にこだわっていて、有用なコンパイラー拡張がない場合、それを処理するための特にクリーンな方法はありません。私が使用していたテクニックは:
#define TRACE(x) do { if (DEBUG) dbg_printf x; } while (0)
次に、コードに次のように記述します。
TRACE(("message %d\n", var));
二重括弧は非常に重要です。これが、マクロ展開に面白い表記がある理由です。以前と同様に、コンパイラーは常にコードの構文の妥当性をチェックします(これは適切です)が、オプティマイザーは、DEBUGマクロがゼロ以外と評価された場合にのみ印刷機能を呼び出します。
これには、「stderr」などを処理するためのサポート関数(例ではdbg_printf())が必要です。varargs関数の書き方を知っている必要がありますが、それは難しくありません。
#include <stdarg.h>
#include <stdio.h>
void dbg_printf(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}
もちろん、この手法はC99でも使用できますが__VA_ARGS__
、二重括弧のハックではなく、通常の関数表記を使用しているため、この手法は見事です。
[ 別の回答に対するコメントの再ハッシュ。]
上記のC99とC89の両方の実装の背後にある中心的なアイデアの1つは、コンパイラ本体が常にデバッグ用のprintfのようなステートメントを見るということです。これは、長期的なコード(10〜2年続くコード)にとって重要です。
コードの一部が何年もの間ほとんど休止状態(安定)だったが、今は変更する必要があるとします。デバッグトレースを再度有効にします。ただし、長年の安定したメンテナンス中に名前が変更または再入力された変数を参照しているため、デバッグ(トレース)コードをデバッグするのは面倒です。コンパイラー(ポストプリプロセッサー)が常に印刷ステートメントを確認する場合は、周囲の変更によって診断が無効になっていないことを確認します。コンパイラーがprintステートメントを認識しない場合、自分自身の不注意(または同僚や共同作業者の不注意)からユーザーを保護することはできません。「を参照してくださいプログラミングの実践カーニハンとパイク、特に第8章で」(上もウィキペディアを参照してくださいTPOP)。
これは「そこにある、それを終えた」経験です。私は本質的に、非デバッグビルドで数年(10年以上)にわたってprintfのようなステートメントが表示されない他の回答で説明されている手法を使用しました。しかし、私はTPOPでアドバイスを見つけ(以前のコメントを参照)、何年か経ってからいくつかのデバッグコードを有効にし、変更されたコンテキストがデバッグを壊すという問題に遭遇しました。何度も、印刷を常に検証することで、後の問題から私を救いました。
NDEBUGを使用してアサーションのみを制御し、別のマクロ(通常はDEBUG)を使用して、デバッグトレースをプログラムに組み込むかどうかを制御します。デバッグトレースが組み込まれている場合でも、デバッグ出力を無条件に表示したくないことがよくあります。そのため、出力を表示するかどうかを制御するメカニズムがあります(デバッグレベル。fprintf()
直接呼び出すのではなく、条件付きでのみ印刷するデバッグ印刷関数を呼び出します。そのため、コードの同じビルドで、プログラムオプションに基づいて印刷することも印刷しないこともできます)。より大きなプログラム用のコードの「マルチサブシステム」バージョンもあるので、ランタイム制御下で、プログラムのさまざまなセクションでさまざまな量のトレースを生成できます。
すべてのビルドで、コンパイラーが診断ステートメントを表示する必要があることを私は主張しています。ただし、デバッグが有効になっていない限り、コンパイラはデバッグトレースステートメントのコードを生成しません。基本的に、それはあなたのすべてのコードがあなたがコンパイルするたびにコンパイラーによってチェックされることを意味します-リリースであろうとデバッグであろうと。これは良いことです!
/*
@(#)File: $RCSfile: debug.h,v $
@(#)Version: $Revision: 1.2 $
@(#)Last changed: $Date: 1990/05/01 12:55:39 $
@(#)Purpose: Definitions for the debugging system
@(#)Author: J Leffler
*/
#ifndef DEBUG_H
#define DEBUG_H
/* -- Macro Definitions */
#ifdef DEBUG
#define TRACE(x) db_print x
#else
#define TRACE(x)
#endif /* DEBUG */
/* -- Declarations */
#ifdef DEBUG
extern int debug;
#endif
#endif /* DEBUG_H */
/*
@(#)File: $RCSfile: debug.h,v $
@(#)Version: $Revision: 3.6 $
@(#)Last changed: $Date: 2008/02/11 06:46:37 $
@(#)Purpose: Definitions for the debugging system
@(#)Author: J Leffler
@(#)Copyright: (C) JLSS 1990-93,1997-99,2003,2005,2008
@(#)Product: :PRODUCT:
*/
#ifndef DEBUG_H
#define DEBUG_H
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
/*
** Usage: TRACE((level, fmt, ...))
** "level" is the debugging level which must be operational for the output
** to appear. "fmt" is a printf format string. "..." is whatever extra
** arguments fmt requires (possibly nothing).
** The non-debug macro means that the code is validated but never called.
** -- See chapter 8 of 'The Practice of Programming', by Kernighan and Pike.
*/
#ifdef DEBUG
#define TRACE(x) db_print x
#else
#define TRACE(x) do { if (0) db_print x; } while (0)
#endif /* DEBUG */
#ifndef lint
#ifdef DEBUG
/* This string can't be made extern - multiple definition in general */
static const char jlss_id_debug_enabled[] = "@(#)*** DEBUG ***";
#endif /* DEBUG */
#ifdef MAIN_PROGRAM
const char jlss_id_debug_h[] = "@(#)$Id: debug.h,v 3.6 2008/02/11 06:46:37 jleffler Exp $";
#endif /* MAIN_PROGRAM */
#endif /* lint */
#include <stdio.h>
extern int db_getdebug(void);
extern int db_newindent(void);
extern int db_oldindent(void);
extern int db_setdebug(int level);
extern int db_setindent(int i);
extern void db_print(int level, const char *fmt,...);
extern void db_setfilename(const char *fn);
extern void db_setfileptr(FILE *fp);
extern FILE *db_getfileptr(void);
/* Semi-private function */
extern const char *db_indent(void);
/**************************************\
** MULTIPLE DEBUGGING SUBSYSTEMS CODE **
\**************************************/
/*
** Usage: MDTRACE((subsys, level, fmt, ...))
** "subsys" is the debugging system to which this statement belongs.
** The significance of the subsystems is determined by the programmer,
** except that the functions such as db_print refer to subsystem 0.
** "level" is the debugging level which must be operational for the
** output to appear. "fmt" is a printf format string. "..." is
** whatever extra arguments fmt requires (possibly nothing).
** The non-debug macro means that the code is validated but never called.
*/
#ifdef DEBUG
#define MDTRACE(x) db_mdprint x
#else
#define MDTRACE(x) do { if (0) db_mdprint x; } while (0)
#endif /* DEBUG */
extern int db_mdgetdebug(int subsys);
extern int db_mdparsearg(char *arg);
extern int db_mdsetdebug(int subsys, int level);
extern void db_mdprint(int subsys, int level, const char *fmt,...);
extern void db_mdsubsysnames(char const * const *names);
#endif /* DEBUG_H */
カイル・ブラントは尋ねました:
とにかくこれを行う
debug_print
には、引数がない場合でもまだ機能しますか?例えば:debug_print("Foo");
シンプルで古風なハックが1つあります。
debug_print("%s\n", "Foo");
以下に示すGCCのみのソリューションも、そのサポートを提供します。
ただし、次のコマンドを使用して、ストレートC99システムでそれを行うことができます。
#define debug_print(...) \
do { if (DEBUG) fprintf(stderr, __VA_ARGS__); } while (0)
最初のバージョンと比較して、 'fmt'引数を必要とする限定的なチェックが失われます。つまり、誰かが引数なしで 'debug_print()'を呼び出そうとする可能性があります(ただし、引数リストの末尾のコンマはfprintf()
コンパイルに失敗します)。 。チェックの喪失がまったく問題であるかどうかは議論の余地があります。
一部のコンパイラは、マクロで可変長引数リストを処理する他の方法の拡張機能を提供する場合があります。具体的には、Hugo Idelerのコメントで最初に述べたように、GCCでは、マクロの最後の「固定」引数の後に通常表示されるコンマを省略できます。また##__VA_ARGS__
、マクロ置換テキストで使用することもできます。これにより、前のトークンがコンマである場合に限り、表記の前のコンマが削除されます。
#define debug_print(fmt, ...) \
do { if (DEBUG) fprintf(stderr, fmt, ##__VA_ARGS__); } while (0)
このソリューションは、フォーマット後にオプションの引数を受け入れながら、フォーマット引数を要求するという利点を保持しています。
この手法は、GCC互換性のためにClangでもサポートされています。
do while
ここの目的は何ですか?
関数呼び出しのように見えるようにマクロを使用できるようにする必要があります。つまり、セミコロンが後に続きます。したがって、それに合わせてマクロ本体をパッケージ化する必要があります。を含まないif
ステートメントを使用するとdo { ... } while (0)
、次のようになります。
/* BAD - BAD - BAD */
#define debug_print(...) \
if (DEBUG) fprintf(stderr, __VA_ARGS__)
ここで、次のように書いたとします。
if (x > y)
debug_print("x (%d) > y (%d)\n", x, y);
else
do_something_useful(x, y);
残念ながら、プリプロセッサはこれと同等のコードを生成するため、インデントはフローの実際の制御を反映していません(インデントと中括弧は実際の意味を強調するために追加されています)。
if (x > y)
{
if (DEBUG)
fprintf(stderr, "x (%d) > y (%d)\n", x, y);
else
do_something_useful(x, y);
}
マクロでの次の試みは次のようになります。
/* BAD - BAD - BAD */
#define debug_print(...) \
if (DEBUG) { fprintf(stderr, __VA_ARGS__); }
そして、同じコードの断片は今生成します:
if (x > y)
if (DEBUG)
{
fprintf(stderr, "x (%d) > y (%d)\n", x, y);
}
; // Null statement from semi-colon after macro
else
do_something_useful(x, y);
そして、これelse
は構文エラーです。do { ... } while(0)
ループは、これらの問題の両方を回避することができます。
うまくいくかもしれないマクロを書くもう一つの方法があります:
/* BAD - BAD - BAD */
#define debug_print(...) \
((void)((DEBUG) ? fprintf(stderr, __VA_ARGS__) : 0))
これにより、プログラムフラグメントが有効として表示されます。(void)
しかし、それはどこカンマ演算子の左オペランドとして使用することができ-それは値が必要とされるコンテキストで使用されているキャスト防止do { ... } while (0)
バージョンができません。デバッグコードをそのような式に埋め込むことができるはずだと思うなら、これを好むかもしれません。完全なステートメントとして機能するようにデバッグプリントを要求する場合は、do { ... } while (0)
バージョンの方が適しています。マクロの本文にセミコロン(大まかに言えば)が含まれる場合は、do { ... } while(0)
表記のみを使用できることに注意してください。それは常に機能します。式ステートメントのメカニズムは、適用するのがより難しい場合があります。回避したい式の形式でコンパイラから警告が表示されることもあります。コンパイラと使用するフラグによって異なります。
TPOPは以前はhttp://plan9.bell-labs.com/cm/cs/tpopとhttp://cm.bell-labs.com/cm/cs/tpopにありましたが、どちらも現在(2015-08-10)です。壊れた。
あなたのしている好奇心ならば、あなたは私の中でのGitHubでこのコードを見ることができますSOQファイルとして(スタックオーバーフローの質問)リポジトリdebug.c
、debug.h
およびmddebug.c
中
のsrc / libsoqの
サブディレクトリ。
#define debug(...) \ do { if (DEBUG) \ printk("DRIVER_NAME:"); \ printk(__VA_ARGS__); \ printk("\n"); \ } while (0)
__FILE__, __LINE__, __func__, __VA_ARGS__
、printfパラメータがない場合、つまり単に呼び出した場合はコンパイルされません 。## __ VA_ARGS__を使用すると、関数にパラメータを渡せないため、debug_print("Some msg\n");
これを修正できますfprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, __LINE__, __func__, ##__VA_ARGS__);
。
#define debug_print(fmt, ...)
と#define debug_print(...)
。これらの最初の引数には、少なくとも1つの引数、フォーマット文字列(fmt
)、および0個以上の他の引数が必要です。2番目には、合計で0個以上の引数が必要です。前者とdebug_print()
一緒に使用すると、マクロの誤用についてプリプロセッサからエラーが表示されますが、2番目ではエラーが発生しません。ただし、置換テキストが有効なCではないため、コンパイルエラーは引き続き発生します。したがって、実際にはそれほど大きな違いはありません。したがって、「制限されたチェック」という用語を使用します。
-D input=4,macros=9,rules=2
、入力システムのデバッグレベルを4に設定し、マクロシステムを9に設定できます(詳細な調査が行われています) )とルールシステムを2に。テーマには無限のバリエーションがあります。あなたに合ったものを使用してください。
私はこのようなものを使用します:
#ifdef DEBUG
#define D if(1)
#else
#define D if(0)
#endif
プレフィックスとしてDを使用するだけではありません。
D printf("x=%0.3f\n",x);
コンパイラーはデバッグコードを認識し、コンマの問題はなく、どこでも機能します。またprintf
、配列をダンプしたり、プログラム自体に冗長な診断値を計算したりする必要がある場合など、十分でない場合にも機能します。
編集:わかりました、else
この注入されたものによって傍受される可能性のある近くにどこかにある場合、問題が発生する可能性がありif
ます。これはそれを越えるバージョンです:
#ifdef DEBUG
#define D
#else
#define D for(;0;)
#endif
for(;0;)
、D continue;
またはのようなものを書くと問題が発生する可能性がありますD break;
。
移植可能な(ISO C90)実装では、次のように二重括弧を使用できます。
#include <stdio.h>
#include <stdarg.h>
#ifndef NDEBUG
# define debug_print(msg) stderr_printf msg
#else
# define debug_print(msg) (void)0
#endif
void
stderr_printf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
int
main(int argc, char *argv[])
{
debug_print(("argv[0] is %s, argc is %d\n", argv[0], argc));
return 0;
}
または(ハッキー、それをお勧めしません)
#include <stdio.h>
#define _ ,
#ifndef NDEBUG
# define debug_print(msg) fprintf(stderr, msg)
#else
# define debug_print(msg) (void)0
#endif
int
main(int argc, char *argv[])
{
debug_print("argv[0] is %s, argc is %d"_ argv[0] _ argc);
return 0;
}
私は次のようなことをします
#ifdef DEBUG
#define debug_print(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
#else
#define debug_print(fmt, ...) do {} while (0)
#endif
これはもっときれいだと思います。
assert()
stdlibからも同じように機能し、通常NDEBUG
は自分のデバッグコードにマクロを再利用します...
http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.htmlによると、##
前にあるはず__VA_ARGS__
です。
そうし#define dbg_print(format, ...) printf(format, __VA_ARGS__)
ないと、マクロは次の例をコンパイルしませんdbg_print("hello world");
。
#define debug_print(FMT, ARGS...) do { \
if (DEBUG) \
fprintf(stderr, "%s:%d " FMT "\n", __FUNCTION__, __LINE__, ## ARGS); \
} while (0)
これは私が使用するものです:
#if DBG
#include <stdio.h>
#define DBGPRINT printf
#else
#define DBGPRINT(...) /**/
#endif
追加の引数がなくても、printfを適切に処理することには大きな利点があります。DBG == 0の場合、最も馬鹿げたコンパイラーでさえ、何もかみ合わないため、コードは生成されません。
以下の私のお気に入りはですvar_dump
。
var_dump("%d", count);
次のような出力を生成します:
patch.c:150:main(): count = 0
@ "Jonathan Leffler"の功績です。すべてC89に満足しています。
コード
#define DEBUG 1
#include <stdarg.h>
#include <stdio.h>
void debug_vprintf(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}
/* Call as: (DOUBLE PARENTHESES ARE MANDATORY) */
/* var_debug(("outfd = %d, somefailed = %d\n", outfd, somefailed)); */
#define var_debug(x) do { if (DEBUG) { debug_vprintf ("%s:%d:%s(): ", \
__FILE__, __LINE__, __func__); debug_vprintf x; }} while (0)
/* var_dump("%s" variable_name); */
#define var_dump(fmt, var) do { if (DEBUG) { debug_vprintf ("%s:%d:%s(): ", \
__FILE__, __LINE__, __func__); debug_vprintf ("%s = " fmt, #var, var); }} while (0)
#define DEBUG_HERE do { if (DEBUG) { debug_vprintf ("%s:%d:%s(): HERE\n", \
__FILE__, __LINE__, __func__); }} while (0)
だから、gccを使うとき、私は好きです:
#define DBGI(expr) ({int g2rE3=expr; fprintf(stderr, "%s:%d:%s(): ""%s->%i\n", __FILE__, __LINE__, __func__, #expr, g2rE3); g2rE3;})
コードに挿入できるからです。
デバッグしようとしているとしましょう
printf("%i\n", (1*2*3*4*5*6));
720
次に、次のように変更できます。
printf("%i\n", DBGI(1*2*3*4*5*6));
hello.c:86:main(): 1*2*3*4*5*6->720
720
また、どの式が何に対して評価されたかを分析できます。
二重評価の問題から保護されていますが、gensymがないため、名前の衝突が発生しやすくなっています。
ただし、ネストします。
DBGI(printf("%i\n", DBGI(1*2*3*4*5*6)));
hello.c:86:main(): 1*2*3*4*5*6->720
720
hello.c:86:main(): printf("%i\n", DBGI(1*2*3*4*5*6))->4
したがって、変数名としてg2rE3を使用しない限り、大丈夫だと思います。
確かに、私はそれ(および文字列の関連バージョン、デバッグレベルのバージョンなど)が非常に貴重であることを発見しました。
私はこれを何年も行う方法を煮詰めてきましたが、最終的に解決策を考え出しました。しかし、私はすでに他の解決策がここにあることを知りませんでした。まず、Lefflerの回答との違いは、デバッグ出力を常にコンパイルする必要があるという彼の主張がわかりません。私がテストする必要があり、それらが最適化されていない可能性がある場合、不要なときに、プロジェクトで大量の不要なコードを実行したくありません。
毎回コンパイルすることは、実際よりも悪く聞こえるかもしれません。時にはコンパイルされないデバッグ出力が必要になりますが、プロジェクトを完成させる前にコンパイルしてテストすることはそれほど難しくありません。このシステムでは、3レベルのデバッグを使用している場合は、デバッグメッセージレベル3に設定し、コンパイルエラーを修正して、コードを完成させる前に他のエラーがないか確認します。(もちろん、デバッグステートメントのコンパイルは、意図したとおりに機能していることを保証するものではありません。)
私のソリューションでは、デバッグの詳細レベルも提供しています。最高レベルに設定すると、すべてコンパイルされます。最近高いデバッグ詳細レベルを使用している場合、それらはすべてその時点でコンパイルできました。最終更新は非常に簡単です。私は3つ以上のレベルは必要としませんでしたが、ジョナサンは彼が9つを使用したと言います。この方法(レフラーの方法など)は、任意の数のレベルに拡張できます。私の方法の使い方はもっと簡単かもしれません。コードで使用する場合、必要なステートメントは2つだけです。ただし、CLOSEマクロもコーディングしていますが、何もしません。ファイルに送信していたのかもしれません。
コストに対して、配信前にコンパイルされることを確認するためにテストする追加のステップは、
ブランチは、最近のプリフェッチプロセッサでは実際にはかなりコストがかかります。アプリがタイムクリティカルなアプリでなければ、大したことではないかもしれません。しかし、パフォーマンスが問題である場合、はい、そうです。やや高速に実行するデバッグコードを選択することを選択するのに十分な大きさです(そして、注記したように、まれに、より高速なリリースを選択します)。
したがって、私が欲しかったのは、印刷されない場合はコンパイルされませんが、印刷される場合はコンパイルするデバッグ印刷マクロです。また、デバッグのレベルも必要だったので、たとえば、コードのパフォーマンスにとって重要な部分を時々印刷せずに、他の場所で印刷したい場合は、デバッグレベルを設定して、追加のデバッグプリントを開始できます。印刷がコンパイルされているかどうかを判断するデバッグレベルを実装する方法に出くわしました。私はこのようにしてそれを達成しました:
// FILE: DebugLog.h
// REMARKS: This is a generic pair of files useful for debugging. It provides three levels of
// debug logging, currently; in addition to disabling it. Level 3 is the most information.
// Levels 2 and 1 have progressively more. Thus, you can write:
// DEBUGLOG_LOG(1, "a number=%d", 7);
// and it will be seen if DEBUG is anything other than undefined or zero. If you write
// DEBUGLOG_LOG(3, "another number=%d", 15);
// it will only be seen if DEBUG is 3. When not being displayed, these routines compile
// to NOTHING. I reject the argument that debug code needs to always be compiled so as to
// keep it current. I would rather have a leaner and faster app, and just not be lazy, and
// maintain debugs as needed. I don't know if this works with the C preprocessor or not,
// but the rest of the code is fully C compliant also if it is.
#define DEBUG 1
#ifdef DEBUG
#define DEBUGLOG_INIT(filename) debuglog_init(filename)
#else
#define debuglog_init(...)
#endif
#ifdef DEBUG
#define DEBUGLOG_CLOSE debuglog_close
#else
#define debuglog_close(...)
#endif
#define DEBUGLOG_LOG(level, fmt, ...) DEBUGLOG_LOG ## level (fmt, ##__VA_ARGS__)
#if DEBUG == 0
#define DEBUGLOG_LOG0(...)
#endif
#if DEBUG >= 1
#define DEBUGLOG_LOG1(fmt, ...) debuglog_log (fmt, ##__VA_ARGS__)
#else
#define DEBUGLOG_LOG1(...)
#endif
#if DEBUG >= 2
#define DEBUGLOG_LOG2(fmt, ...) debuglog_log (fmt, ##__VA_ARGS__)
#else
#define DEBUGLOG_LOG2(...)
#endif
#if DEBUG == 3
#define DEBUGLOG_LOG3(fmt, ...) debuglog_log (fmt, ##__VA_ARGS__)
#else
#define DEBUGLOG_LOG3(...)
#endif
void debuglog_init(char *filename);
void debuglog_close(void);
void debuglog_log(char* format, ...);
// FILE: DebugLog.h
// REMARKS: This is a generic pair of files useful for debugging. It provides three levels of
// debug logging, currently; in addition to disabling it. See DebugLog.h's remarks for more
// info.
#include <stdio.h>
#include <stdarg.h>
#include "DebugLog.h"
FILE *hndl;
char *savedFilename;
void debuglog_init(char *filename)
{
savedFilename = filename;
hndl = fopen(savedFilename, "wt");
fclose(hndl);
}
void debuglog_close(void)
{
//fclose(hndl);
}
void debuglog_log(char* format, ...)
{
hndl = fopen(savedFilename,"at");
va_list argptr;
va_start(argptr, format);
vfprintf(hndl, format, argptr);
va_end(argptr);
fputc('\n',hndl);
fclose(hndl);
}
使用するには、次のようにします。
DEBUGLOG_INIT("afile.log");
ログファイルに書き込むには、次のようにします。
DEBUGLOG_LOG(1, "the value is: %d", anint);
それを閉じるには、次のようにします。
DEBUGLOG_CLOSE();
現在のところ、技術的には何もしないので、これは必要さえありません。現時点ではCLOSEをまだ使用していますが、それがどのように機能するかについて気が変わって、ロギングステートメント間でファイルを開いたままにしたい場合に備えます。
次に、デバッグ印刷をオンにする場合は、ヘッダーファイルの最初の#defineを編集して、たとえば次のようにします。
#define DEBUG 1
ロギングステートメントを何もコンパイルしないようにするには、次のようにします。
#define DEBUG 0
頻繁に実行されるコード(つまり、詳細レベルの高いコード)の情報が必要な場合は、次のように記述します。
DEBUGLOG_LOG(3, "the value is: %d", anint);
DEBUGを3と定義すると、ロギングレベル1、2、3がコンパイルされます。2に設定すると、ロギングレベル1および2が取得されます。1に設定すると、ロギングレベル1のステートメントのみが取得されます。
do-whileループについては、これはifステートメントではなく、単一の関数または何も評価されないため、ループは必要ありません。OK、C ++ IOの代わりにCを使用するように私にご注意ください(そしてQtのQString :: arg()もQtで変数をフォーマットする安全な方法です—かなり洗練されていますが、より多くのコードを必要とし、フォーマットのドキュメントは整理されていませんそれはそうかもしれません-しかし、それでも私はそれが望ましいケースを見つけました)、しかしあなたはあなたが望む.cppファイルにどんなコードでも置くことができます また、クラスの場合もありますが、インスタンス化してそれを維持するか、new()を実行して保存する必要があります。このように、#include、init、およびオプションでcloseステートメントをソースにドロップするだけで、それを使用する準備が整います。しかし、もしあなたがそんなに傾いているなら、それは素晴らしいクラスになるでしょう。
私は以前に多くの解決策を見てきましたが、これだけでなく私の基準に合うものもありませんでした。
それほど重要ではないが、それに加えて:
DEBUGLOG_LOG(3, "got here!");
; したがって、たとえばQtのより安全な.arg()フォーマットを使用できます。MSVCで動作するため、おそらくgccで動作します。sで使用##
されますが#define
、これはLefflerが指摘するように非標準ですが、広くサポートされています。(##
必要に応じて使用しないように再コーディングできますが、彼が提供するようなハックを使用する必要があります。)警告:ログレベルの引数を指定し忘れた場合、MSVCは識別子が定義されていないと主張してしまいます。
一部のソースはそのシンボルも定義しているため、DEBUG以外のプリプロセッサシンボル名を使用することもできます(たとえば./configure
、ビルドを準備するコマンドを使用するprogsなど)。それを開発したとき、私には自然に思えました。DLLが別のアプリケーションで使用されているアプリケーションで開発しましたが、ログプリントをファイルに送信するのがより一般的です。しかし、それをvprintf()に変更してもうまくいきます。
これにより、デバッグロギングを行うための最良の方法を考え出すことについて多くの人が悲しみを解消できることを願っています。またはあなたが好むかもしれないものを示しています。私は中途半端にこれを何十年も理解しようと努力してきました。MSVC 2012および2015で動作するため、おそらくgccで動作します。おそらく他の多くのプロジェクトにも取り組んでいますが、それらについてはテストしていません。
私もこの日のストリーミング版を作るつもりです。
注:Lefflerに感謝します。Lefflerは、StackOverflowに合わせてメッセージをより適切にフォーマットするために協力してくれました。
((void)0)
—簡単です。
このテーマのバリエーションは、カテゴリーごとに個別のマクロ名を必要とせずにデバッグカテゴリーを提供すると思います。
プログラムスペースが32Kに制限され、動的メモリが2Kに制限されているArduinoプロジェクトでこのバリエーションを使用しました。デバッグステートメントとトレースデバッグ文字列を追加すると、すぐに領域が消費されます。したがって、コードがビルドされるたびに、コンパイル時に含まれるデバッグトレースを必要最小限に制限できることが不可欠です。
#ifndef DEBUG_H
#define DEBUG_H
#define PRINT(DEBUG_CATEGORY, VALUE) do { if (DEBUG_CATEGORY & DEBUG_MASK) Serial.print(VALUE);} while (0);
#endif
#define DEBUG_MASK 0x06
#include "Debug.h"
...
PRINT(4, "Time out error,\t");
...