QtとReactive Extensions

[Qt Advent Calendar 2016](http://qiita.com/advent-calendar/2016/qt) 3日目の記事です。

# 要約
[QtのシグナルとイベントをObservableに変換するライブラリ](https://github.com/tetsurom/rxqt)を[RxCpp](https://github.com/Reactive-Extensions/RxCpp)で作りました。

# Reactive Extensions

Reactive Extensions(Rx)の解説はすでに無数にあるので省略します。

* [なぜReactive Extensionsを導入するのか？](http://blog.mmmcorp.co.jp/blog/2016/07/22/why-rx/)

ここでは、RxのC++実装である[RxCpp](https://github.com/Reactive-Extensions/RxCpp)を使用します。

# Observableとシグナル

ObservableとQtのシグナル/スロットは、どちらもObserverパターンである点で同じですが、RxではObservableから新たなObservableを作る関数が豊富に用意されているという利点があります。また、言語を超えて同じRxのAPIが使えるというのも(Rxを知っている人には)学習コストが低くなり嬉しいです。

そこで、シグナルをObservableへ変換します。

```cpp
auto lineedit = new QLineEdit;

rxcpp::observable<QString> textChangedStream = rxcpp::observable<>::create<QString>(
    [lineedit](rxcpp::subscriber<QString> s){
        QObject::connect(
            lineedit,
            &QLineEdit::textChanged,
            [s](const QString& v){ s.on_next(v); }
        );
    }
);
```

RxCppで`observable<T>`を作るファクトリー関数の1つに`create`関数があります。`create`関数は`subscriber<T>`を受け取る関数を引数に取ります。渡した関数の中で引数の`subscriber<T>`の`on_next`関数に渡した値が`observable<T>`に流れます。

上の例では、`s.on_next("Hello Observable");`とすることで`"Hello Observable"`が流れます。

あとはシグナルが来た時に`on_next`を呼べばよく、シグナルを`on_next`を呼ぶラムダ式へ`connect`することで`observable<T>`に変換できます。

# Observableとイベント

Qtにはシグナルの他にイベントもあります。イベントもObservableに変換できれば、シグナルとイベントを意識せずにObservableとして扱うことができて便利そうです。

Qtでイベントハンドリングをする方法は、イベントハンドラーをオーバーライドする方法の他に、外部からイベントフィルターを設定してハンドリングする方法があります。

次のようなイベントフィルターを作っておき、

```cpp
class EventFilter: public QObject {
public:
    EventFilter(QObject* parent, QEvent::Type type, rxcpp::subscriber<QEvent*> s)
        : QObject(parent), type(type), s(s) {}
    ~EventFilter(){
        s.on_completed();
    }

    bool eventFilter(QObject* obj, QEvent* event){
        if(event->type() == type){
            s.on_next(event);
        }
        return QObject::eventFilter(obj, event);
    }
private:
    QEvent::Type type;
    rxcpp::subscriber<QEvent*> s;
};
```

目的のQObjectにインストールすることでイベントをObservableに変換できます。

```cpp
auto lineedit = new QLineEdit;

rxcpp::observable<QEvent*> keyPressStream = rxcpp::observable<>::create<QEvent*>(
    [](rxcpp::subscriber<QEvent*> s){
        lineedit->installEventFilter(new EventFilter(lineedit, QEvent::KeyPress, s));
    }
);
```

# RxQt

シグナルとイベントをObservableに変換する処理をその都度書くのは大変なのでライブラリ化しました。

* [RxQt](https://github.com/tetsurom/rxqt)

## 注意
RxCppが動けば大丈夫だと思いますが、まだVisual C++ 2015でしか試していません。

## API

### from_signal

```cpp
template<class Q, class Ret>
rxcpp::observable<long>
rxqt::from_signal(QObject* object, Ret(Q::*)(void))

template<class Q, class Ret, class Arg>
rxcpp::observable<std::remove_const<std::remove_reference<Arg>::type>::type
rxqt::from_signal(QObject* object, Ret(Q::*)(Arg))

template<class Q, class Ret, class ...Args>
rxcpp::observable<std::tuple<std::remove_const<std::remove_reference<Args>::type>::type...>
rxqt::from_signal(QObject* object, Ret(Q::*)(Args...))
```

シグナルをObservableに変換します。**observableの型はシグナルの引数の型から推論されます**。ただし、引数についていた参照とconstは外れます。

* 0引数の場合  
    rxcppで`observable<void>`は作れないため、`observable<long>`になります(値はシグナルが届いた回数)。 
* 1引数の場合  
    `observable<(引数の型)>` になります。
* n引数の場合(n>1)  
    `observable<std::tuple<(引数の型)>>` になります。

```cpp:example
auto lineedit = new QLineEdit;

// returnPressedシグナルの型は void (QLineEdit::*)(void) なので、
// observable<long> になる
rxcpp::observable<long> returnPressedStream
    = rxqt::from_signal(lineedit, &QLineEdit::returnPressed)

// textChangedシグナルの型は void (QLineEdit::*)(const QString&) なので、
// observable<QString> になる
rxcpp::observable<QString> textChangedStream
    = rxqt::from_signal(lineedit, &QLineEdit::textChanged);

// cursorPositionChangedシグナルの型は void (QLineEdit::*)(int, int) なので、
// observable<std::tuple<int, int>> になる
rxcpp::observable<std::tuple<int, int>> cursorPositionChangedStream
    = rxqt::from_signal(lineedit, &QLineEdit::cursorPositionChanged);
```

### from_event

```cpp
rxcpp::observable<QEvent*>
rxqt::from_event(QObject* object, QEvent::Type type)
```

イベントをObservableに変換します。

```cpp:example
auto lineedit = new QLineEdit;

rxqt::from_event(lineedit, QEvent::KeyPress)
    .subscribe([](const QEvent* e){
        auto ke = static_cast<const QKeyEvent*>(e);
        qDebug() << ke->key();
    });
```

## サンプル

2つのボタンを2つともクリックしたら「両方押した」と出力するサンプルです。

```cpp:example
auto button1 = new QPushButton("A");
auto button2 = new QPushButton("B");

rxqt::from_signal(button1, static_cast<void(QPushButton::*)(bool)>(&QPushButton::clicked))
    .zip(rxqt::from_signal(button2, static_cast<void(QPushButton::*)(bool)>(&QPushButton::clicked)))
    .subscribe([](auto&&){ qDebug() << "両方押した"; });
```

(オーバーロードされているとメンバー関数ポインタに対して`static_cast`が必要になるのが少々残念)

# まとめ
シグナルとイベントをObservableに変換することでQtでRxが使えます。