イントロダクション
Qt開発において信号とスロットは重要な概念ですが、その内部動作を理解することはより高度な開発に不可欠です。本記事では、Qtの信号スロットシステムがどのように実装されているかを詳細に解説します。
MOC(メタオブジェクトコンパイラ)の役割
QtのMOCは、Q_OBJECTマクロが含まれるクラス宣言を解析し、メタオブジェクト情報を生成する専用のプリプロセッサです。コンパイル時にmoc_xxx.cppファイルを生成し、以下の要素を提供します。
生成されるメタデータ構造
// メタ文字列データ構造体
struct MetaStringData {
const char *stringData;
const uint *uintData;
};
// メタオブジェクトデータ
static const QMetaObject::Data metaData = {
// クラス情報、メソッド情報などを格納
};
自動生成されるメタ関数
Q_OBJECTマクロにより以下の関数が自動実装されます:
metaObject()- クラスのメタオブジェクトを返すqt_metacast()- 動的キャストをサポートqt_metacall()- メタメソッド呼び出しを処理qt_static_metacall()- 静的なメタメソッド呼び出し
接続処理の内部実装
connect関数は内部的にConnectionオブジェクトを作成し、送信者の接続リストに追加します:
struct Connection {
QObject *sender;
int signalIndex;
QObject *receiver;
int methodIndex;
int methodOffset;
ConnectionType type;
CallbackFunction callback;
Connection *next;
};
void addConnection(int signalIndex, Connection *conn) {
connectionList[signalIndex].append(conn);
}
信号発火時の処理フロー
信号が発火されると、以下の処理が行われます:
直接接続(Direct Connection)
同一スレッド内では直接的な関数呼び出しとして処理されます:
void activateSignal(QObject *sender, int signalIndex, void **args) {
ConnectionList &list = sender->d_func()->connectionList[signalIndex];
for (Connection *conn : list) {
if (conn->type == DirectConnection) {
conn->callback(conn->receiver, args);
}
}
}
キュー接続(Queued Connection)
異なるスレッド間ではイベントキューを介した非同期処理が行われます:
void queueActivation(QObject *sender, int signalIndex, void **args) {
MetaCallEvent *event = new MetaCallEvent(sender, signalIndex, args);
QCoreApplication::postEvent(receiver, event);
}
実装の要点
- 信号は宣言のみで実装はMOCが自動生成
- スロットは通常のメンバ関数として実装が必要
- 接続情報は送信者が管理する接続リストに格納
- 異なる接続方式で同期/非同期の呼び出しを実現