Turn On Blog

ぺーぺーSEの色々メモ

【Qt】定義したクラス・構造体の内容をQDebugで出力する

QDebug、便利ですよね。
本日は自分で定義したクラスや構造体の内容をQDebugで出力する方法について。

以下の様なStudentクラスがあるとします.

class Student
{
    private:
        QString name_;
        int age_;
        int japanese_;
        int math_;
    // メソッド等は省略
}

この内容をQDebugに出力したいな~なんてとき、手っ取り早くやるとしたら以下のようにすると思います。

qDebug() << "name" << student.getName();
qDebug() << "age" << student.getAge();
qDebug() << "japanese" << student.getJapanese();
qDebug() << "math" << student.getMath();

しかしこれではStudentクラスに不必要なgetterを用意しなくてはなりません。
そのうえ、Studentクラスの内容がふえるたびにdebug部分を修正する必要がでてきます。
凝集性が低くてよくないですね。
(ほんとうに一回見たいだけとか一部のメンバを見たいだけだったらいいかもしれませんが)

そこで、以下のようにできれば便利です。

qDebug() << student;

が、おそらくそのままでは以下のエラーになります。
ストリーム演算子が無いよ~と言われてます。定義してないので当然ですね。

エラー: no match for 'operator<<' (operand types are 'QDebug' and 'Student')
  qDebug() << student;
           ^

では、定義していきましょう。

QDebugと独自クラスのストリーム演算子の宣言・定義

宣言

まず、宣言は以下のようにクラス内で行います。

friend QDebug operator<<(QDebug dbg, const Student &student);

ここでの注意点は、定義する関数はStudentのメンバ関数してはいけないということです。
ここで定義したいオペレータ演算子はあくまでQDebugクラスのものです。
Studentのメンバ関数としてしまうと解決ができなくなってしまうため、グローバル関数として定義します。
このあたりのことは正しく理解できているかあまり自信がないのですが一般的なストリーム演算子オーバーロードと同じなようです。以下サイトを御覧ください。
C++編(言語解説) 第35章 グローバルな演算子オーバーロード

定義

定義は、クラス外で行います。
以下をStudent.hないしStudent.cppのグローバルなところに記述しましょう。

QDebug operator<<(QDebug dbg, const Student &c)
{
	// QDebugの状態を保存
	QDebugStateSaver saver(dbg);

        // スペース出力しないよう設定
	dbg.nospace();

	// 出力
	dbg << "Student(";
	dbg << "name : " << c.name_ << ", ";
	dbg << "age : " << c.age_ << ", ";
	dbg << "japanese : " << c.japanese_ << ", ";
	dbg << "math : " << c.math_;
	dbg << ")";

	// QDebugオブジェクトを返す
	return dbg;
}

メンバ変数の出力本体をStudent( ... )で囲っているのはQtの標準クラスのデバッグ出力に倣ってみました。

余談ですが、この出力のところを書くのもけっこうめんどうなので、
以下の様な秀丸マクロを定義して自動でやっています。

setcompatiblemode 0x0F;
// クリップボードの内容を貼付け
paste;
begingroupundo;

// 置換実行
replaceall "(\\t|    ).* (.*)_;" , "dbg << \"\\2 : \" << c.\\2_ << \",\";" , regular, nocasesense, hilight, loop;
if( ! result )  beep;
endgroupundo 1;

// 全選択してカット
selectall;
cut;

メンバ変数の宣言部分をクリップボードにコピーした状態でこのマクロを実行すると、
dbg << ~の形式におきかえてくれます。
※ ただしこのマクロがちゃんと動くには以下の条件が揃っている必要があります
・operator<<の独自クラスの変数名はcであること
・QDebugの変数名はdbgであること
・独自クラスのメンバ変数は末尾に_がついてること
・独自クラスのメンバ変数の型にスペースが含まれないこと(long longとかはダメ)

うごかしてみよう

以上の宣言と定義をすませたうえで、以下コードを実行してみましょう。

Student student("StudentName", 12, 100, 23);
qDebug() << student;

おそらく、以下の様な出力があるはずです。

Student(name : "StudentName", age : 12, japanese : 100, math : 23)

コーディング規約などで、定義するクラスにはこの関数を定義することをさだめておくと、
デバッグするときに非常にらくなのでおすすめです。