はじめに
この記事は2025年4月に書いた記事です。
DxLibなどを使ってゲームを作っていると、std::coutが使えなくてとても不便だと感じました。ゲーム画面にデバッグ情報を出力しても良いのですが、ゲームループ内でDrawFormatString()などを使ってデバッグ情報を出力すると、値が毎フレーム変わるような変数を出力した際に目視がとても困難になります。また、値の推移などを確認することも難しいうえに、さまざまな要素を出力しようとすると、ゲーム画面のかなりの部分がデバッグ情報で埋まってしまい、視認性が下がります。この問題を解消するために、std::coutと同じ要領で使えるけど、出力はデバッグに回されるdebug::coutを作っていきます。
実装
#pragma once
#include <sstream>
#include <windows.h>
namespace debug
{
class DebugStream : public std::ostream
{
class DebugBuffer : public std::stringbuf
{
public:
int sync() override
{
OutputDebugStringA(this->str().c_str());
this->str("");
return 0;
}
};
DebugBuffer buf;
public:
DebugStream() :std::ostream(&buf) {}
};
inline DebugStream cout;
inline std::ostream& endl(std::ostream& os)
{
os.put('\n');
os.flush();
return os;
}
}
解説
・std::ostreamを継承するDebugStreamは、内部にstd::stringbuf型のオブジェクトを保有します。このオブジェクトは送られてきた文字列をためておくバッファーとして機能します。親からsyncメソッドが呼び出されるとその内容を出力し、バッファーを空にします。std::stringbufを継承するDebuBufferは、syncメソッドをoverrideしており、標準出力の代わりにデバッグ出力へ回すようにしています。基底クラスであるstd::ostreamクラスでは、<<演算子がオーバーロードされており、これをそのま使用してバッファーに情報を送ります。
・コンストラクタではどのオブジェクトをバッファーとして使用するのかという情報を引数とし、基底クラスのコンストラクタを呼び出します。
・DebugStream型のオブジェクトをinline変数として定義します。inlineではなくstaticで良いのでは?と思われるかもしれませんが、static変数は翻訳単位ごとにインスタンスが生成されてしまいます。このクラスはプログラム全体で唯一のオブジェクトを共有したほうが便利です。例えば、ファイルAで管理している変数とファイルBで管理している変数を一行で表示したいとします。この時にオブジェクトが共有されていなければ、Aの内容を<<でcoutに送り込んだ後、Bの内容をcoutに送り込み、その後endlしたとしてもBの内容しか出力されません。inline変数ならこの問題を解決することができます。
・endlメソッドは、バッファーに改行文字を出力し、flushを実行します。flushの内部ではstd::stringbufのsyncメソッドが呼び出されます。返り値としてstd::ostream型の参照を返すようにしています。このようにすることで、以下のようなチェーン操作が可能になります。
debug::cout<<"A"<<debug::endl<<"B"<<debug::endl;
解説は以上です。
使い方
#include "DebugStream.h"
int main()
{
debug::cout<<"Hello,World"<<debug::endl;
}
本当にこのクラスは必要?
わざわざこんなものを実装しなくても、デバッグ出力したいときにOutputDebugStringA()を直接呼び出せば良いのでは?と思われるかもしれません。しかし、OutputDebugStringA()はCスタイルの文字列しか受け取れないという明確な欠点があります。そのため、整数や浮動小数点数を出力したければ、stringを作成してからc_str()して渡すか、printf()のように出力変換指定子をいちいち記述する必要があります。これはとても冗長で不便です。全体で35行程度と非常に安いコストでこの問題を解決できるので、導入する価値が高いと感じます。