C言語講座11回目。マクロでデバッグです。
C言語のプリプロセッサは、実に強力という話で、前回は関数マクロを話題にしましたが、それを使った有効なデバッグ方法についてです。
関数マクロでデバッグを楽にする方法

デバッガは、ソースコードレベルで、この行まで実行というのが普通ですが、それでも最強のデバッグ方法は、やはり必要なところにプリント文をいれて確認することだと思います。
もちろん、デバッグしてすぐにわかる場合は、問題ないのだけれど、なかなかわからないバグに悩まされているときは、一度ひとつずつプリント文を入れて追ってみるのも、早期解決につながります。
出力結果を見て、推理すること、どれがバグを早く発見するために重要です。
結果は、ログファイルにでも保存しておけば、テスト結果を残すことにもつながり一石二鳥にもなります。
マクロによるデバッグ方法
#ifdefでくくってデバッグ文を記述する
#ifdef _MYDEBUG
{
FILE* fp;
fp = fopen("debug.log", "a");
if (fp != NULL) fprintf(fp, "(%s)-(%s)\n", item1, item2);
fclose(fp);
}
#endif
時には、このようなデバッグの痕跡を、プログラムの至るところに埋めこんでいるソースコードを見かけます。
相当大変なバグに悩まされたのかも知れませんが、通常は必要のない命令がコードに埋め込まれていると、可読性も落ちて維持するのが大変になります。
こんなソースコードを追っていると、どこが正常ルートで、やりたいことが何なのか、いつのまにかデバッグ文の影に隠れて薄れてしまい、一体何をしたいプログラムなのかわからなくなってしまいます。
次のようなマクロで対応すれば、プログラムはトレースをいれてもすっきりします。
[mydebug.h]
-------------------
#ifdef _MYDEBUG
void MyDebugTrace(const char* szFmt, ...);
#define DEBUG_TRACE0(s0) MyDebugTrace((s0))
#define DEBUG_TRACE1(s0,s1) MyDebugTrace((s0),(s1))
#define DEBUG_TRACE2(s0,s1,s2) MyDebugTrace((s0),(s1),(s2))
....
#else
#define DEBUG_TRACE0(s0) /* 何もしない */
#define DEBUG_TRACE1(s0,s1) /* 何もしない */
#define DEBUG_TRACE2(s0,s1,s2) /* 何もしない */
....
#endif
-------------------
[mydebug.c]
#ifdef _MYDEBUG
void MyDebugTrace(const char* fmt, ...)
{
FILE* fp;
int rtn;
va_list prmlst;
va_start(prmlst, fmt);
if ((fp = fopen("debug.log", "a")) == NULL) {
va_end(prmlst);
return;
}
vfprintf(fp, fmt, prmlst);
fclose(fp);
va_end(prmlst);
}
#endif
-------------------
上記関数は、出力先ログファイル名称は固定であるが、設定できるようにしたり、ログをクリアする関数を作ったり、そのあたりは工夫次第だと思います。
実際の使用例
int main()
{
char szItem1[128];
DEBUG_TRACE0("main start\n");
....
DEBUG_TRACE1("Item1 = <%s>\n", szItem1);
....
DEBUG_TRACE0("main end\n");
}
コンパイル時に _MYDEBUG が定義されていればトレースが有効になるし、定義されていなければ、トレース文は消滅状態というわけです。
マクロはプリプロセッサで完全に取り除かれてますので、
トレース文は、引数の数さえ気をつければ、printf 文のように使用できます。
トレース文を挿入するところ全てで、#ifdef _MYDEBUG ~ #endif が埋めこまれている場合と比較してみて欲しい。
そして、プログラムを書く場合、プロジェクト上問題がないのであれば、最初からテストを想定してある程度のトレース情報をコード上に記述しておくことをお勧めします。
最初は、デバック状態にしておき、最後は、コンパイル時のdefine 定義を変えるだけで、ソースコードには変更を加えずともログの出力されないバージョンが完成します。