Qtのsignal/slotとthread(2)

前回に引き続き、Qtのsignal/slotとthreadの話。

と言っても、メインのスレッドとQThreadで作成したスレッドで、同じデータを触りたいときは、普通の並行プログラミングと変わらない。
QtのAPIのドキュメントに、thread-safeと書いてない限りは、QMutex等を使って自分で排他をする必要がある。

しかし、Qtを使っている場合は、わざわざ自分でMutexの管理をしなくても、スレッドとのデータのやり取りを全て signal/slotでやってしまい、共有データを持たないようにすれば、Mutexの出番はないのだ。

そこで、今回はsignal/slotでやり取りできるデータについて書いていくことにする。

ソースは、[github](https://github.com/false-git/qtsamples/tree/ThreadDemo2/ThreadDemo)に上げてある。

# int
いわゆるJavaで言うプリミティブタイプ。C++だとintとかlongとかdoubleとか。
こう言った型は、値渡しでコピーされるので、何も気にしなくて良い。

```cpp:mythread.h
        emit intResult(1);
```

```cpp:mainwindow.cpp
    connect(t, &MyThread::intResult, this, [](int result) {
        qDebug() << QThread::currentThreadId() << "intResult:" << result;
    });
```

# QString
この例では、`const QString &` にしてあるが、`QString`でもあまり変わらない。(コストは・・・。QStringはmutableなので、使い方によっては違いがある・・・、けどそんな使い方しないよね？)

```cpp:mythread.h
        QString r("abc");
        emit stringResult(r);
```

```cpp:mainwindow.cpp
    connect(t, &MyThread::stringResult, this, [](const QString &result) {
        qDebug() << QThread::currentThreadId() << "stringResult:" << result;
    });
```

# MyObject
自前のクラス(QObjectのサブクラスでない)のオブジェクト。

```cpp:mythread.h
        MyObject o;
        o.i = 3;
        emit objectResult(o);
```

```cpp:mainwindow.cpp
    connect(t, &MyThread::objectResult, this, [](MyObject result) {
        qDebug() << QThread::currentThreadId() << "objectResult:" << result.i;
    });
```
自前のオブジェクトをsignal/slotで渡すときには、上記のようにそのまま書くとうまくいかない。

```cpp:mythread.h
Q_DECLARE_METATYPE(MyObject)
```

```cpp:mainwindow.cpp
    qRegisterMetaType<MyObject>();
```
上記二つのおまじないが必要で、かつ自前のクラスは以下の条件を満たす必要がある。

* publicなデフォルトコンストラクタを持つ
* publicなコピーコンストラクタを持つ
* publicなデストラクタを持つ

ここまでの条件が必要な上、emitで1回、slotの呼び出しで1回のコピーが発生するので、今回のサンプルでは4回デストラクタが呼ばれている。(参照にすれば2回で済むかも？)
よほどの事情がない限り、この形を使うことはないだろう。

# MyObject*
オブジェクトのコピーを嫌うのであれば、オブジェクトのポインタを渡せば良い。この場合は、MetaTypeの登録も不要である。

```cpp:mythread.h
        MyObject *p = new MyObject;
        p->i = 5;
        emit object_pResult(p);
```

```cpp:mainwindow.cpp
    connect(t, &MyThread::object_pResult, this, [](MyObject *result) {
        qDebug() << QThread::currentThreadId() << "object_pResult:" << result->i;
    });
```
このやり方には問題がある。
new で確保したMyObjectは決してdeleteされない。
ポインタで受け渡す以上、メモリの管理をどちらが行うかは、決めておく必要がある。しかし、emitする側はいつslotが呼び出されるかわからないため、実質的に解放するタイミングがわからない。
必然的に、slot側でdeleteすることになるが、一つのsignalに複数のslotをconnectすることが可能なので、自分が一番最後に呼ばれたslotかどうかがわからないとやはりdeleteするタイミングがわからないことになる。
connectの引数で渡すConnectionTypeに、Qt::UniqueConnectionを||で指定すると、複数のslotがconnectされることを防げるが、**connectされていない場合**にはやはりリークが発生する。

# QSharedPointer\<MyObject>
QSharedPointerは、c++11のstd::shared_ptrとほぼ同じだと思って良い。試していないが、`CONFIG += c++11` してあれば、QSharedPointerの代わりにstd::shared_ptrを使っても動くと思う。

```cpp:mythread.h
        QSharedPointer<MyObject> sp(new MyObject);
        sp->i = 7;
        emit object_spResult(sp);
```

```cpp:mainwindow.cpp
    connect(t, &MyThread::object_spResult, this, [](QSharedPointer<MyObject> result) {
        qDebug() << QThread::currentThreadId() << "object_spResult:" << result->i;
        result->i = 9;
    });
    connect(t, &MyThread::object_spResult, this, [](QSharedPointer<MyObject> result) {
        qDebug() << QThread::currentThreadId() << "object_spResult2:" << result->i;
    });
```
このケースでも、`Q_DECLARE_METATYPE(QSharedPointer<MyObject>)`の宣言と、`qRegisterMetatype<QSharedPointer<MyObject>>()`の呼び出しが必要になる。
上のサンプルでは、同じsignalに対して2回connectを実行している。
Qtのsignal/slotのルールは、複数connectした場合は、**一つ一つ順番に**、**connectした順序で**slotが呼び出される。
上のサンプルでは、わざわざ最初のslotでMyObjectの中身を変更しているが、2番目に呼ばれるslotでは変更後のオブジェクトにアクセスすることになる。
イベント伝搬の仕組みでも、イベントハンドラの中でQEvent::accept()とかでイベントの状態を変えたりするけど、slotをconnectの順番に依存したり、引数を変えたりするのはオススメしない。

後は、QObjectのサブクラスの場合、QWidgetのサブクラスの場合、QList等のQObjectのサブクラスでないQtのコンテナ型の場合、等いろいろ考えられるが、力尽きたので今回はここまで。

特に、QObjectには thread affinity と言う概念があり、別スレッドでnewしたオブジェクトは親子関係にすることができない。この辺については、QObjectのマニュアルの`moveToThread()`周りを読んで欲しい。
気が向いたら書くと思うが、続きではなく別の機会に。