Notes C API & C++11+ & ReactiveX & Qt #2
リアクティブ・エクステンション(Reactive Extensions、ReactiveX、RX)は奥が深く、勉強を始めてから日の浅い私もまだまだわからないことだらけです。ですが、初級レベルくらいならある程度書けるようになってきたので、Notes APIを例に、RXを適用するとこんな風になるというところをご紹介してみます。
ここでは、サーバ名を取得する関数「NSGetServerList」関数を使って、一般的な方法と、RXを使った方法とを比較してみます。NSGetServerList関数については、昨年書いたこちらをご覧下さい。
これをDirというクラスの静的メソッドとして実装したとします。そして、そのメソッドを呼び出して、サーバ名のリストを取得する側のコードを書いてみます。私のように、20年以上オブジェクト指向でプログラムを書いてきた場合、次のようになるでしょう。
namespace nx { using namespace chiburu::noteslib; } QList<nx::String> serverList = nx::Dir::getServerList(); QTextStream sout(stdout); foreach (nx::String server, serverList) { sout << server.toQString() << endl; }
名前空間やクラス名などのこまかいところはさておき、getServerListで取得したサーバ名リストをQList<nx::String> serverListに保管し、標準出力にサーバ名を出力します。極めて一般的な書き方ではないでしょうか。 (※nx(=chiburu::noteslib)名前空間がついたクラスが独自のもので、特にnx::StringはLMBCS文字列を表しています。)
このnx::Dir::getServerListメソッドを、リアクティブ・エクステンションで実装し直すと、こんな書き方になります。
QTextStream sout(stdout); nx::Dir::getServerListStream() .subscribe([&](nx::String server) { sout << server.toQString() << endl; });
&{...}という書き方は、C++11以降で使える「ラムダ式」で、JavaScriptでいうクロージャのようなもので、リアクティブ・エクステンションに直接関係はないですが、これがないとC++でRXは立ちゆかないでしょう。
getServerList関数は、getServerListStreamと名前を変えました。シグネチャは次のようになります。
#include <rxcpp/rx.hpp> namespace Rx { using namespace rxcpp; } Rx::observable<nx::String> nx::Dir::getServerListStream(nx::String port = nx::String());
Rx::observable?いきなり出てこられても困りますが、ここでは、「LMBCS文字列を流してくるもの」というふわっとした感じで捉えておいて下さい。「流してくる」ので「捕らえれば」サーバ名を取得できる・・・という具合です。じゃあ、どこで「捕らえる」か?「subscribe」で「捕らえます」。「subscribe」は「observable」のメソッドです。「observable」が流すものを、「subscribe」で捕らえて、指定したラムダ式や関数オブジェクトなどで処理します。
リアクティブ・エクステンションを使うと何が嬉しいか?検索するといろいろ議論になっていますが、ここでは局所的に捉えてみます。
今回のNSGetServerList関数をラップする場合、取得したサーバ名を返すとき、サーバ名が一括で返ってきてしまうので、リストという形でまとめ上げ、それを返すことになります。テンプレートを使ったとしても、すべてのサーバ名を「なんらかのコンテナオブジェクト」に収めてから返すことに変わりはありません。
リアクティブ・エクステンションを使って、取得したサーバ名を、順次呼び出し側に提供するようにすると、コンテナオブジェクトを強要することがなくなりました。呼び出し側は、サーバ名の扱い方を自分で決めることができるようになります。これだけでも十分すごいことだと、実装してみて感じました。
今回のようにNotes APIを使った例では、同期的なプログラムが大半を占めますが、この仕組みは非同期的なプログラムでも使えます。特にHTTPリクエストやUI更新など非同期を多く使う場面では、リアクティブ・エクステンションは威力を発揮するようです。同期、非同期に関係なく使えることはとても大きいと感じています(実際には意識する必要がでてきますが)。