C++ /CX と WinRTに深く潜るというタイトルのセッションで Marian Luparu氏は、Windows 8をターゲットにしたC++アプリケーションの例外処理とパフォーマンスについて講演した。開発者が理解すべき最も重要なことは、 いかにWinRTと通常のC++の境界が例外処理とパフォーマンスに効果をもたらすかである。
基本的な例外処理
WinRTは、例外の概念を持っていない、それは内部的にHRESULTを使用している。Platform::Exception から継承したクラスに入れられたデバッグ情報は、ABI境界を超えて移動しない。通常、例外に関連付けらたメッセージは、デバッガのみで利用可能 だ。
もしそれらがABI境界を超えたら、C++ と SEHスタイルの例外は、プロセスを終了させる。すなわち、全てのC++例外は、処理される必要がある。例え、Platform::Exceptionでそれをラップしてもである。
COMException使用を捕捉した際に常にHRESULTを確認すること。もしあるHRESULTを処理する方法がわからない場合は、例外を再スローする。同様に、あなたはCOMメソッドを呼び出して、エラーコードを返す場合、それは直ちに例外になる必要がある。
非同期と例外処理
Parallel Patterns Library (PPL)を使って開発している時、ラムダ式が結果でなくタスクを受け取ることが重要だ。例えば、以下を使う。
.then( [](Task<int> result) {…} )
次式ではない。
.then( [](int result) {…} )
もし2つ目のバージョンを使い、例外が発生すれば、 “then”ブロックは完全にスキップされる。
非同期とスレッド
C#やVBのasync/await シンタックスのように、PPLはスレッドとなると、難しい。もしUIスレッドで操作を始めたら、それぞれの継続( “then”ブロック)は、またUIスレッド上でスケジュールされる。もしそうでなく、スレッドプールを使いたければ、task_continuation_context::use_arbitraryの結果を “then”メソッドに渡す必要がある。
C++クラスとWinRTクラス
一般的に開発者は、強制されない限り、WinRTスタイルのクラス(refクラス)を使用するべきではない。なぜなら彼らは、XAMLとやり取りしたり、他の言語が使えるようにWinRTコンポーネントを公開するからである。WinRTクラスは、ポータブルでない通常のC++クラスより遅い、すなわち普通のC++アプリケーションでそれらを使うことはできない。
パフォーマンスを改善する別の方法は、WinRT と C++の型の間での変換を減らすことである。WinRT境界を跨いで頻繁に通信すれば、遅くなってしまうだろう。不要なメモリの割当てやデータのコピーは、更に遅くなる。
StringReferenceのような幾つかのラッパークラスは、パフォーマンスの助けになるだろう。しかし注意がいる。 StringReferenceは参照カウントされるので、それがラップするバッファは、 StringReferenceが破棄されるまで変更したり、削除できない。この問題が起きるのは、WinRTが StringReferenceのコピーを手放さない時であるが、これは必ずしも予測できるものではない。 Marian Luparu氏は他の例を上げている。
- StringReferenceはバッファをラップするために作成される。
- WinRT関数が StringReferenceを使って呼ばれる
- WinRT関数がC++コールバックを呼び出す
- コールバックが基になるバッファを変更する。
- 制御がWinRT関数に戻される。関数は予期せずに変更された文字列を持つことになる。
ArrayReferenceはC++の配列のラッパーとして、同じように使用することができる。しかし、WinRTはC++の配列が不変であることを期待していないので、ひどいことになる可能性は低い。