C++11 の std::function を使って signal-slot ライクにメンバー関数をコールバック

この記事の補足。 std::function を使って Qt の signal-slot みたくメンバー関数をコールバックする方法あれこれ。

例によって擬似タイマー

(1) 関数スコープ内で定義したラムダ式を connect

(2) メンバー変数定義したラムダ式を connect

(3) std::bind で this ポインターを bind して connect

(4) メンバー関数 –> std::function アダプターを自作

(1)~(4) は全部こんな感じで使用可能

他にもまだやり方があるかもしれませんが、とりあえず思いつくだけ。

Qt 5.4.0 を MinGW 64bit (MinGW-w64) で ビルドする

Qt5.4.0 + x86_64-4.9.2-win32-seh-rt_v3-rev1 でビルドしました。 基本的に 「Qt 5.3.1 を MinGW 64bit (MinGW-w64) で ビルドする」 と同じでいけるようです。

今回のビルド用に修正したものです。 前の記事では軽くビルドに 1~2時間かかると書きましたが、今回夜中の 2時頃に仕掛けて、朝7時に見たら終わってませんでした… ビルドは出かける前か寝る前に仕掛けるのが良さそうです。

C++11 の可変長テンプレート引数を利用した Qt ライクな signal-slot の実装

「今更何を言うか」と言う気がしないでもないですが、google 先生に聞いても意外にヒットしないようなので書いてみます。 C++ は強い型付けの言語であり、(暗黙的に渡される this ポインターの事を考えれば当然と言えば当然なのですが…) たとえ引数の数と型が同じであっても違うクラスに属するメンバー関数の型は違うものとして扱われます。これは型安全の面から考えると大変有難い仕様である一方、あるクラスに所有されているオブジェクトから所有元オブジェクトへのイベント通知を考えた場合、このメンバー関数の型がクラスの型に依存するという点が障害になってきます。この障害を乗り越えてメンバー関数へのコールバックを実現する代表的な方法として

  1. 通常の関数ポインターの口に静的メンバー関数を登録。 void* 型にキャストした所有元クラスの this ポインターを引数としてコールバックさせて、静的メンバー関数内で元のクラスの型に cast する
  2. 所有元クラスがイベント通知元のインスタンスが要求するインターフェースを継承する(GoF の Mediator パターンがこのタイプ)
  3. 通知元のオブジェクトのテンプレートポリシーに所有元クラスを指定してダックタイピング

などがありますが、同じ型の通知元インスタンスが複数あり、かつインスタンスごとに所有元クラスの違うメンバー関数を呼び分けたいような場合、何れのケースにおいてもコールバック先(所有元クラスのメンバー関数内)で呼び出し元インスタンスの特定(インスタンスのアドレス比較、id 比較など)を行う必要があり、お世辞にもエレガントな方法であるとは言えません。 また 1 と 2 の方法の派生で、「複数の静的メンバー関数を用意して使い分ける」「自分ではなく専用に用意した内部クラスにインターフェースを継承させ、内部クラスに持たせた this ポインター経由で自分のメソッドを呼ばせる」 etc… などすれば自由に通知元インスタンスと所有元クラスメンバー関数の対応付けを行う事ができますが、これはこれで美しいかと言われれば微妙です…

Qt で導入された signal / slot のメカニズムは、通常のプリプロセス–>コンパイル–>リンクという実行ファイル生成の過程に moc (meta object compiler) による自動コード生成を追加することで、「クラスの型や同種の他のオブジェクトの存在に影響を受けない簡潔、柔軟かつ疎結合なメンバー関数コールバックの仕組み」を実現した点が非常に画期的だった訳です。

ただ、signal / slot のような仕組みが Qt の専売特許だったかと言えばそんな事はなく、C++03 の時代にも Boost.Signals などを初め、Qt ライクな signal-slot の実装は存在していましたが、 moc のような仕組みを利用せずに pure C++ の守備範囲で実装しようとした場合、プリプロセッサメタプログラミングを行う事はほぼ必須でした。 しかし C++11 からは可変長テンプレート引数の仕組みが導入されたため、プリプロセッサによる力技偉大なテクニックを借りる必要がなくなり、比較的コンパクトにクラスの型に依存しない通知の仕組みを実現できるようになりました。 例えばこんな感じ… 実際に使用してみるとこんな感じ もちろん Qt の signal / slot の方が高機能でリッチなのは言うまでもありませんが、必要最低限の通知の仕組みはたった 100 行程度のコードで実現出来てしまいます。 ただ、同じく C++11 で導入された std::function を使えば標準ライブラリの範囲内で簡潔に同じような事が出来てしまいますので(std::function を使った例についてはこちら)、今更敢えてこんなコードを書く必要は無いかもしれませんが…

VMWare 上の linux mint 17 に Qt の開発環境を構築する

敢て記事にするほどの事では無いかもしれませんが….

VMware 上での mint 17 のインストールは完了しているとの前提で。
「VMWare 上の」と書いていますが、やってみていないだけで mint 17 を直インストールしている場合も同様の手順でイケるはずです。

(1) Qt 開発環境の導入
まず qt-project のページで Qt5.3.2 for linux のインストーラをダウンロードします。

http://www.qt.io/download-open-source/qt-linux-installer-download

次に落としてきたインストーラに実行権限を与えて実行します。

$chmod u+x qt-opensource-linux-x86-5.3.2.run
$sudo ./qt-opensource-linux-x86-5.3.2.run

GUI のインストーラが立ち上がりますので、画面の指示に従ってインストールします。

(2) g++ を導入
Qt の開発には g++ が必要なので導入します。

$sudo apt-get install g++

(3) opengl を導入
Qt ウィジットアプリケーションの開発には opengl が必要ですので opengl も導入します。

$sudo apt-get install update
$sudo apt-get install freeglut3-dev

ちなみに opengl を入れない状態でビルドすると libGL が見つからない「cannot find -lGL」と言って怒られます。(当たり前ですが….)

(4) Qt Creator の起動
1 で指定したインストール先の Qt5.3.2/Tools/QtCreator/bin/qtcreator.sh を実行すると Qt Creator が起動しますので、後は通常通り poject を作成して開発を行います。

Qt で作成したアプリケーションを Windows 向けに deploy する

クリーンな windows 環境で Qt アプリケーションを動作させるためには SDK の DLL や開発環境の runtime を持っていくだけでは不十分で実はほかにも色々とファイルが必要になります。
Windows 向けの Qt SDK には windeployqt.exe と言う実行ファイルの依存関係を解析して必要なファイルを集めてくれるツールが標準で付属しており、(Qt 関係のファイルに限ってですが)このツールを使うと面倒な deploy の作業を自動化することが出来ます。

Qt for Windows – Deployment

※ちなみに正しく deploy 出来ていない状態で実行ファイルを dependency walker にかけると以下の DLL が未解決になるようです。

  • API-MS-WIN-APPMODEL-RUNTIME-L1-1-0.DLL
  • API-MS-WIN-CORE-WINRT-ERROR-L1-1-0.DLL
  • API-MS-WIN-CORE-WINRT-L1-1-0.DLL
  • API-MS-WIN-CORE-WINRT-ROBUFFER-L1-1-0.DLL
  • API-MS-WIN-CORE-WINRT-STRING-L1-1-0.DLL
  • API-MS-WIN-SHCORE-SCALING-L1-1-1.DLL

例えば sample で付属している analogclock のプロジェクトを deploy するにはプロジェクトの release 出力ディレクトリへ移動し以下のようにします。

$windeployqt --release --libdir d:/deploy analogclock.exe

ちなみに mingw でビルドしたサンプルをこのコマンドでdeploy すると、 d:/deploy フォルダーの下には以下のようなファイルが集められます。

deploy

実行ファイル自身や他に使用している 3rd party 製のライブラリ DLL などは置いてけぼりですので、必要な物があれば別途持ってきましょう。

 

自分でビルドした Qt SDK を Qt Creator で使用する

※今回使用している Qt Creator の version は 3.1.2です。

自前でビルドした Qt SDK は Qt Creator に自動認識されないようですので、使用するには手動で追加する必要があります。

・ デバッガ (gdb) の登録

・コンパイラの登録

追加ボタンを押して MinGW を選択します。

・SDK の登録

bin ディレクトリの下にある qmake.exe を登録します。

※ ビルドの際に –prefix オプションで指定したディレクトリと異なる場合 「Qt バージョンが正しくインストールされていません。 make install を実行してください」 と表示されるようです。

・Kit の作成

以上の作業でビルドしてた SDK が使用できるようになります。