23

この質問はこのフォーラムですでに行われていますが、私はその概念を理解していません。

私が読んでいたところ、シグナルとスロットは関数ポインターを使用して実装されているようです。つまり、シグナルは、接続されているすべてのスロット(関数ポインター)を呼び出す1つの大きな関数です。これは正しいです?そして、ストーリー全体で生成されたmocファイルの役割は何ですか?信号関数がどのスロットを呼び出すか、つまりどのスロットがこの信号に接続されているかをどのように認識しているかわかりません。

御時間ありがとうございます

4

2 に答える 2

16

Qtは、インタプリタ言語に似た方法でこれらを実装します。つまり、信号名を関数ポインタにマップするシンボルテーブルを作成し、それらを維持し、必要に応じて関数名で関数ポインタを検索します。

信号を発するたびに、つまり書き込み

emit something();

実際にsomething()関数を呼び出します。この関数は、メタオブジェクトコンパイラによって自動的に生成され、*.mocファイルに配置されます。この関数内で、この信号が現在接続されているスロットがチェックされ、適切なスロット関数(独自のソースで実装した)がシンボルテーブルを介して(上記の方法で)順番に呼び出されます。またemit、他のQt固有のキーワードと同様に、*.moc生成後にC++プリプロセッサによって破棄されます。実際、Qtヘッダーの1つ(qobjectdefs.h)には、次のような行があります。

#define slots 
#define signals protected
#define emit

接続関数(connect)は、ファイル内に保持されているシンボルテーブルを変更するだけ*.mocであり、それに渡される引数(SIGNAL()および `SLOTマクロを使用)も、テーブルに一致するように前処理されます。

それが一般的な考え方です。彼または彼女の別の回答では、ジョージはtrolltechメーリングリストへのリンクとこのトピックに関する別のSO質問へのリンクを提供しています。

于 2009-09-10T22:10:37.750 に答える
5

以下を追加する必要があると思います。

別のリンクされた質問があります-そして、その回答に対する非常に詳細な拡張と見なすことができる非常に優れた記事があります。これがこの記事です、コード構文の強調表示が改善されています (まだ完全ではありません)。

ここに私の短い言い直しがありますが、それは間違いを起こしやすいかもしれません )

基本的に、クラス定義にマクロを挿入するQ_OBJECTと、プリプロセッサはそれをQMetaObject同じクラスのすべてのインスタンスで共有される静的インスタンス宣言に展開します。

class ClassName : public QObject // our class definition
{
    static const QMetaObject staticMetaObject; // <--= Q_OBJECT results to this

    // ... signal and slots definitions, other stuff ...

}

このインスタンスは、初期化時にシグナルとスロットのシグネチャ( ) を保存し、呼び出しを実装できるようにします。これにより、シグネチャ文字列によってメソッドのインデックスが返されます。"methodname(argtype1,argtype2)"indexOfMethod()

struct Q_CORE_EXPORT QMetaObject
{    
    // ... skip ...
    int indexOfMethod(const char *method) const;
    // ... skip ...
    static void activate(QObject *sender, int signal_index, void **argv);
    // ... skip ...
    struct { // private data
        const QMetaObject *superdata; // links to the parent class, I guess
        const char *stringdata; // basically, "string1\0string2\0..." that contains signatures and other names 
        const uint *data; // the indices for the strings in stringdata and other stuff (e.g. flags)
        // skip
    } d;
};

がQtクラス ヘッダーmocのファイルを作成すると、構造体の正しい初期化に必要な署名文字列とその他のデータがそこに配置され、このデータを使用してシングルトンの初期化コードが書き込まれます。moc_headername.cppheadername.hdstaticMetaObject

もう 1 つの重要なことは、オブジェクトのメソッドのコードの生成です。これはqt_metacall()、オブジェクトのメソッド ID と引数ポインターの配列を受け取り、次のswitchように long を介してメソッドを呼び出します。

int ClassName::qt_metacall(..., int _id, void **_args)
{
    // ... skip ...
    switch (_id) {
        case 0: signalOrSlotMethod1(_args[1], _args[2]); break; // for a method with two args
        case 1: signalOrSlotMethod2(_args[1]); break; // for a method with a single argument
        // ... etc ...
    }
    // ... skip ...
}

最後に、 for every signalがcallmocを含む実装を生成します:QMetaObject::activate()

void ClassName::signalName(argtype1 arg1, argtype2 arg2, /* ... */)
{
    void *_args[] = { 0, // this entry stands for the return value
                      &arg1, // actually, there's a (void*) type conversion
                      &arg2, // in the C++ style
                      // ...
                    };
    QMetaObject::activate( this, 
                           &staticMetaObject, 
                           0, /* this is the signal index in the qt_metacall() map, I suppose */ 
                           _args
                         );
}

最後に、connect()呼び出しは文字列メソッド シグネチャを整数 ID ( によって使用されるものqt_metacall()) に変換し、シグナルとスロットの接続のリストを維持します。シグナルが発信されると、コードはこのリストを調べ、メソッドactivate()を介して適切なオブジェクト「スロット」を呼び出します。qt_metacall()

要約すると、静的QMetaObjectインスタンスは「メタ情報」(メソッド シグネチャ文字列など) を格納し、生成されたqt_metacall()メソッドは「メソッド テーブル」を提供し、インデックスによって任意のシグナル/スロットを呼び出すことができます。mocこれらのインデックスを使用して生成されたシグナルの実装を介してactivate()、最後connect()にシグナルからスロットへのインデックスマップのリストを維持する仕事をします。

*注: 異なるスレッド間でシグナルを配信したい場合に使用されるこのスキームは複雑です (blocking_activate()コードを確認する必要があると思われます) が、一般的な考え方が同じであることを願っています)

これは、リンクされた記事に対する私の非常に大まかな理解であり、簡単に間違っている可能性があるため、直接読んでみることをお勧めします )

PS。Qt の実装についての理解を深めたいので、私の言い直しに矛盾があれば教えてください。


私の他の(以前の)回答は熱心な編集者によって削除されたため、ここにテキストを追加します(Pavel Shvedの投稿に組み込まれていない詳細がいくつか欠けており、回答を削除した人が気にかけているとは思えません。)

@パベル・シュヴェド:

Qtヘッダーのどこかに次の行があると確信しています:

#define emit

確認するために、Googleコード検索で古いQtコードで見つけました。まだそこにある可能性が高いです); 見つかった場所のパスは次のとおりです:

ftp://ftp.slackware-brasil.com.br › slackware-7.1› contrib› kde-1.90› qt-2.1.1.tgz› usr› lib› qt-2.1.1› src› kernel› qobjectdefs.h


別の補足リンク: http://lists.trolltech.com/qt-interest/2007-05/thread00691-0.html -- Andreas Pakulat による回答を参照してください。


Qtの質問:シグナルとスロットはどのように機能しますか?

于 2012-03-07T07:49:56.540 に答える