ホーム < ゲームつくろー! < デバッグ技術編

その3 デバッグウィンドウを知らないと大変です


 その1でブレークポイントを設定してプログラムを一時的に止めてステップ実行するデバッグの基本を紹介しました。このデバッグはプログラムが一時停止するので「静的なデバッグ」です。でも、例えばゲームのキーが正しく入力されているかを調べるには、プログラムが動き続けてくれないといけません。また状態遷移などが上手く動いているかをチェックする時にステップ数が膨大になるとデバッグに時間がかかってしまいます。

 静的なデバッグが厳しい状況ではステップ実行は非効率です。そういう時は動かしながらリアルタイムで数値の変化を見るのが得策です。しかし、自動変数ウィンドウなどはステップ実行時以外は見ることができませんので、別の出力を設ける必要があります。その候補の1つがデバッグウィンドウ(出力ウィンドウ)です。

 この章ではデバッグウィンドウの扱い方についてまとめます。



@ デバッグウィンドウ(出力ウィンドウ)へ文字列を出してみよう

 デバッグウィンドウは実は普段良く目にしています。コンパイルした時にコンパイルエラーが出力されているウィンドウ。あれがデバッグウィンドウ(出力ウィンドウ)です。そこにはプログラム上から任意のタイミングで文字列を出力できるんです。

 デバッグウィンドウに文字列を出力するにはOutputDebugString関数を用います:

OutputDebugString関数
#include <windows.h>

void OutputDebugString(
   LPCTSTR lpOutputString
);

lpOutputStringには出力したい文字列へのポインタを渡します。

 使い方はとっても簡単です:

OutputDebugString関数の使用例
int main()
{
   OutputDebugString( _T("エラー発生!\n") );
};

これで出力ウィンドウに同じ文字列が出力されます:


一番下にありますよ


 このように、printfのような感覚でプログラムが動いている最中にも出力ウィンドウに文字列をガンガン出力できるので、リアルタイムのデバッグには非常に重宝するわけです。



A printfのようにOutputDebugString関数を使えないのか?

 OutputDebugString関数はこのようにとても便利なのですが、1つ不便を感じる部分もあります。それは引数に渡すのが「文字列」であるという点です。できる事なら、ここをprintfのように書式付き文字列出力に拡張したいんです。イメージとしてはこんな感じです:

書式付き文字列出力ができるOutputDebugString関数のイメージ
MyOutputDebugString( "現在のベクトルは(%f, %f) \n", vec.x, vec.y );

もちろん引数の数は任意に設定できると良いですね。こんな事が可能なのか?実はマクロをうまく使うとできます

 マクロ(#define)は引数を付ける事ができます。この引数ですが実は「引数省略記号(...)」をつける事ができるんです。これを利用してprintfと同じような書式のマクロ関数を作ります:

書式付きデバッグ文字列出力マクロ
#ifdef _DEBUG
#   define MyOutputDebugString( str, ... ) \
      { \
        TCHAR c[256]; \
        _stprintf( c, str, __VA_ARGS__ ); \
        OutputDebugString( c ); \
      }
#else
#    define MyOutputDebugString( str, ... ) // 空実装
#endif

マクロ関数はデバッグ時にのみ有効にしたいので#ifdefで分けます。MyOutputDebugStringマクロ関数の第2引数をご覧下さい。ここが「...」になっています。これが引数の省略記号です。こう書くとマクロ関数の第2引数以下が可変個となります。これを受けるのが__VA_ARGS__というマクロ名です。こうすると「...」の部分がそっくりそのまま展開されます。これにより_stprintfの第3引数が可変個にできるので、同じ書式を踏襲できるわけです。

 このマクロ関数を使う事で、デバッグ出力は強力に使いやすくなります。例えば次のような使い方です:

関数に入力されてきたベクトルの値をデバッグ出力する
void TestFunc( D3DXVECTOR3 *inVec ) {
   D3DXVECTOR3 tmp( inVec );
   D3DXVec3Normalize( &tmp, &tmp );

   MyOutputDebugString( _T("Vector( %f, %f, %f )\n"), tmp.x, tmp.y, tmp.z );

   *inVec = tmp;
}

出力は例えばこうなります:



 OutputDebugString関数及びその拡張版のマクロ関数を定義する事で、動いている最中のデバック(数値変化)を見ることができるようになりました。これを上手く利用すると、ゲームパッドの入力値を目で見たり、おかしな事が起こった時にデバッグウィンドウにその警告文字列を出したりと、大変便利に動作を追うことができます。これを知っているだけでデバッグ能率は飛躍的に向上するはずです。紹介したマクロ関数はコピペで使えますので、お試し下さい。