![]()
OutputDebugString
Windows には、OutputDebugString というデバッガに文字列を直接送信する API が存在します。
void OutputDebugStringA(const char *lpMessage) ; void OutputDebugStringW(const wchar_t *lpMessage) ;#ifdef _UNICODE#define OutputDebugString OutputDebugStringW#else#define OutputDebugString OutputDebugStringA#endif /* _UNICODE */VC++ に精通している人は、よく利用しているAPIかもしれませんね。
インターフェイスは文字列を単にデバッガへ渡すための簡素なものです。 使う側にとっては、簡素なだけに、少しでも複雑な情報を出力しようとするならば、なんらかの便宜を必ずといって良いほど図らないといけないことを必然的に模索してしまいます。
試しに使ってみましょう。
void RandomAverage() { char buffer[80]; // 一時バッファ 80文字もあれば多分足りるだろう(適当) int average = 0 ; for (int i = 0;i < 1000;i++) { int val = rand() % 1000 ; sprintf(buffer, "%d 回目のランダムな値は、%d です。\n", i+1 , val) ; OutputDebugString(buffer) ; average += val ; } average / = 1000 ; sprintf(buffer, "平均値は、%d です。\n", average) ; OutputDebugString(buffer) ; }上記は、数値をデバッグメッセージとして出力するために sprintf 書式出力文字列を利用して一時バッファに書き換えたものをデバッグメッセージとして出力しています。 これは、かなり面倒な作業ですね。 デバッグメッセージを送信する度に仮バッファを設けてやらなくてはいけません。 簡略化するために、書式出力文字列をダイレクトで出力できる下記のようなデバッグ用の関数があるともっと使い勝手が広がるかもしれません。
#define VSW_BUFSIZE 400 #define VS_BUFSIZE 800 //--------------------------------------------------------------------------- #if defined(__BORLANDC__) && defined(__cplusplus) // dprintf ( for borland c++ ) void dprintf(AnsiString format,...) { AnsiString data; va_list ap; va_start(ap,format); data.vprintf(format.c_str(),ap); va_end(ap); OutputDebugStringA(data.c_str()) ; } //----- // dwprintf ( for borland c++ ) void dwprintf(WideString format,...) { wchar_t data[VSW_BUFSIZE] ; va_list ap ; va_start(ap,format); vswprintf(data,format.c_bstr(),ap); va_end(ap) ; OutputDebugStringW(data) ; } //----- // dprintfln ( for borland c++ ) void dprintfln(AnsiString format,...) { format += "\n" ; AnsiString data; va_list ap; va_start(ap,format); data.vprintf(format.c_str(),ap); va_end(ap); OutputDebugStringA(data.c_str()) ; } //----- // dwprintfln ( for borland c++ ) void dwprintfln(WideString format,...) { format += L"\n" ; wchar_t data[VSW_BUFSIZE] ; va_list ap ; va_start(ap,format); vswprintf(data,format.c_bstr(),ap); va_end(ap) ; OutputDebugStringW(data) ; } //----- #else #if defined(__cplusplus) extern "C" { #endif // dprintf ( for other else ) void dprintf(const char *format,...) { char data[VS_BUFSIZE] ; va_list ap ; va_start(ap,format) ; vsprintf(data,format,ap) ; va_end(ap) ; OutputDebugStringA(data) ; } //----- #if defined(__cplusplus) // dwprintf ( for other else ) void dwprintf(const wchar_t *format,...) { wchar_t data[VSW_BUFSIZE] ; va_list ap ; va_start(ap,format) ; vswprintf(data,format,ap) ; va_end(ap) ; OutputDebugStringW(data) ; } #endif //----- // dprintfln ( for other else ) void dprintfln(const char *format,...) { char data[VS_BUFSIZE] ; va_list ap ; va_start(ap,format) ; vsprintf(data,format,ap) ; va_end(ap) ; OutputDebugStringA(data) ; OutputDebugStringA("\n") ; } //----- #if defined(__cplusplus) // dwprintfln ( for other else ) void dwprintfln(const wchar_t *format,...) { wchar_t data[VSW_BUFSIZE] ; va_list ap ; va_start(ap,format) ; vswprintf(data,format,ap) ; va_end(ap) ; OutputDebugStringW(data) ; OutputDebugStringW(L"\n") ; } #endif //----- #if defined(__cplusplus) } #endif #endif //---------------------------------------------------------------------------デバッグ用の printf という意味を表すことにより、ここではアタマに d という文字を付加した dprintf という系列のものを数種類作ってみました。
dprintf : char 文字対応の printfこれを利用して、先程紹介したサンプル RandomAverage を書き直してみましょう。
dwprintf : wchar_t 文字対応の printf
dprintfln : char 文字対応の最後に改行を自動で付加する printf
dwprintfln : wchar_t 文字対応の最後に改行を自動で付加する printf
void RandomAverage() { int average = 0 ; for (int i = 0;i < 1000;i++) { int val = rand() % 1000 ; dprintfln("%d 回目のランダムな値は、%d です。", i+1 , val) ; average += val ; } average / = 1000 ; dprintfln("平均値は、%d です。", average) ; }いかがでしょうか? かなりスッキリしてきましたね。
vsprintf を利用して丸めこむのも良いのですが(C言語ではこれ以上の簡略化はありえないかもしれません。)、デバッグの際に煩わしい入力作業をあまり増やしたくはないのがデバッグを行う側の心情ではないでしょうか。 上記のソースを見て気付いた人も居るかもしれませんが、出力する文字列の大きさが VS_BUFSIZE / VSW_BUFSIZE を超えた場合、範囲例外が発生してしまいますし、 書式出力文字列を書く場合、%d やら %f などの書式パラメタとは違った値を間違って代入されてしまう危険性も伴います。 それに付け加え、vsprintf を利用する場合、事前に出力されるバッファサイズを計算する手段はなく、適当な領域を割り当ててそこに書き込ませる他に手段がないではありませんか。 筆者はこの問題点を率直に解決できるインターフェイスを思いつきました。 iostream 系の出力ストリームクラスにフェッチして使えるようにしたらもっと便利になるのではないかと。 例えば、下記のようなデバッグ用のストリームクラスを一つ用意しておくともっと使い易くより安全に文字列をデバッガに送る事ができる気がします。
ヘッダ部
// basic_debug_streambuf template <class charT,class traits=std::char_traits<charT> > class basic_debug_streambuf : public std::basic_streambuf<charT,traits> { protected: std::streampos seekoff( std::streamoff off, std::ios::seek_dir dir, int mode = std::ios::in | std::ios::out ) ; std::streampos seekpos( std::streampos pos, int mode = std::ios::in | std::ios::out ) ; traits::int_type overflow( traits::int_type ch = traits::eof() ) ; traits::int_type underflow(void) ; public: basic_debug_streambuf(void) ; ~basic_debug_streambuf(void) ; }; // dstreambuf / wdstreambuf typedef basic_debug_streambuf<char,std::char_traits<char> > dstreambuf ; typedef basic_debug_streambuf<wchar_t,std::char_traits<wchar_t> > wdstreambuf ; // basic_debug_stream template <class charT,class traits=std::char_traits<charT> > class basic_debug_stream : public std::basic_iostream<charT,traits> { basic_debug_streambuf<charT,traits> *debug_streambuf_p ; public: basic_debug_stream(void) : std::basic_iostream<charT,traits>( debug_streambuf_p = new basic_debug_streambuf<charT,traits>() ) {} ~basic_debug_stream(void) { delete debug_streambuf_p ; } }; // dstream / wdstream typedef basic_debug_stream<char,std::char_traits<char> > dstream ; typedef basic_debug_stream<wchar_t,std::char_traits<wchar_t> > wdstream ;ソース部
//--------------------------------------------------------------------------- // basic_debug_streambuf //----- template class basic_debug_streambuf<char,std::char_traits<char> > ; template class basic_debug_streambuf<wchar_t,std::char_traits<wchar_t> > ; //----- template <class charT,class traits> basic_debug_streambuf<charT,traits>::basic_debug_streambuf(void) { setbuf(0,0) ; } //----- template <class charT,class traits> basic_debug_streambuf<charT,traits>::~basic_debug_streambuf(void) { ; } //----- template <class charT,class traits> std::streampos basic_debug_streambuf<charT,traits>::seekoff( std::streamoff off, std::ios::seek_dir dir, int mode ) { return traits::eof() ; } //----- template <class charT,class traits> std::streampos basic_debug_streambuf<charT,traits>::seekpos( std::streampos pos, int mode ) { return traits::eof() ; } //----- template <> std::char_traits<char>::int_type basic_debug_streambuf<char,std::char_traits<char> >::overflow( std::char_traits<char>::int_type ch) { if(ch != std::char_traits<char>::eof()) { char buffer[2]; buffer[0]=ch; buffer[1]=0; OutputDebugStringA(buffer) ; } return 0 ; } //----- template <> std::char_traits<wchar_t>::int_type basic_debug_streambuf<wchar_t,std::char_traits<wchar_t> >::overflow( std::char_traits<wchar_t>::int_type ch) { if(ch != std::char_traits<wchar_t>::eof()) { wchar_t buffer[2] ; buffer[0] = ch ; buffer[1] = 0 ; OutputDebugStringW(buffer) ; } return 0 ; } //----- template <class charT,class traits> traits::int_type basic_debug_streambuf<charT,traits>::underflow(void) { return traits::eof() ; } //---------------------------------------------------------------------------iostream 系のソースは複雑で手が込んでいますが、要はバッファリングを行った所でメリットは全くないため streambuf のコンストラクタで setbuf を利用しバッファ長を0に割り当て、必ずオーバーフローが起きる状態を再現し、オーバーフローしてきた文字をひたすら OutputDebugString に渡すように実装を組んでいるだけのことです。
このストリームクラスを利用して、RandomAverage を書き直してみましょう。
void RandomAverage() { dstream ds ; int average = 0 ; for (int i = 0;i < 1000;i++) { int val = rand() % 1000 ; ds << i+1 << "回目のランダムな値は、" << val << "です。" << endl ; average += val ; } average / = 1000 ; ds << "平均値は、" << average << "です。" << endl ; }ソースの簡略化を図るのであれば、この程度の実装は事前に済ませておきたい所ではないでしょうか。
ちなみに iostream は、最新の VC++ からは消えてなくなっています。 今となっては負の遺産というやつでしょうか。
このアルゴリズムを利用するには微妙な頃合かもしれませんね。