第3回 CライブラリのWindowsランタイム・コンポーネント化戦略:連載:Windowsランタイム・コンポーネントによるコードの再利用(4/5 ページ)
既存のC/C++プログラムをWindowsランタイム・コンポーネント化する際に取れる戦略と、それぞれの特徴や、メリット/デメリットを考察する。
ファサードとしてWindowsランタイム・コンポーネントを作成する
ライブラリをWindowsランタイム・コンポーネントとして利用するもう1つの方法では、ファサード・パターンを適用する。すなわち、元のライブラリのAPIを1対1で忠実に提供するプロキシとは異なり、Windowsランタイム・コンポーネントを利用するアプリケーションに与えたい機能を、よりアプリケーション用にパッケージして提供する方法だ。
例えば、与えられたRubyのソース・コード片を評価し、結果を文字列で取得することを考える。
この場合、mrubyを利用するC++のコードは次のようになる。
mrb_state *mrb = mrb_open();
mrbc_context *c = mrbc_context_new(mrb);
mrb_value v = mrb_load_string_cxt(mrb, Convert(script), c);
mrbc_context_free(mrb, c);
if (mrb->exc) // 例外が発生したら例外を結果とする
{
v = mrb->exc;
}
Platform::String^ str = Convert(v); // mrb_valueをPlatform::Stringへ変換する
mrb_close(mrb);
return str;
mrbc_context*オブジェクトにはmrubyの実行コンテキスト情報が格納される。
Rubyコードの実行はmrb_load_string_cxt関数で行っている。その後、実行コンテキストを解放して、例外の有無をチェックし、結果をPlatform::Stringオブジェクトに変換したものを戻り値として返している。
プロキシとして実装する場合、上記で呼び出しているmrb_*関数およびmrbc_*関数を呼び出すためのAPIをWindowsランタイム・コンポーネントが実装し、アプリケーションがこれを呼び出すことでプロキシを実現できる。
しかし、呼び出し元のWindowsストア・アプリが行いたいのは、単にRubyプログラムを実行して結果の文字列を取得したいことだと限定できるのであれば、先ほど見たようなmrubyのAPIを呼び出すことでmrubyオブジェクトを操作するようなコードを記述するのではなく、以下のようなJavaScriptコードを書けるほうが望ましい。
var x = 10;
var result = Mrb.eval('(1..' + x + ').inject(:*)'); // -> 3628800
JavaScriptでWindowsランタイムコンポーネントに対してmrubyのAPIの操作を指示するのではなく、Windowsランタイムコンポーネントに対してRubyのスクリプトを記述した文字列を与えて、評価結果を得ている。
Mrb.evalメソッドが、mrubyが提供するサービスへの統一的な入口(=ファサード)となっている。
この場合、MrbクラスはmrubyのファサードとしてEvalメソッド(メソッド先頭の大文字はJavaScriptエンジンによって小文字に変換される)のみを公開するように実装できる。
public class Mrb sealed
{
public:
Platform::String^ Eval(Platform::String^ script)
{
mrb_state *mrb = mrb_open();
mrbc_context *c = mrbc_context_new(mrb);
mrb_value v = mrb_load_string_cxt(mrb, Convert(script), c);
mrbc_context_free(mrb, c);
if (mrb->exc) // 例外が発生したら例外を結果とする
{
v = mrb->exc;
}
Platform::String^ str = Convert(v); // mrb_valueをPlatform::Stringへ変換する
mrb_close(mrb);
return str;
}
private:
Platform::String^ Convert(mrb_value str);
char* Convert(Platform::String^ str);
}
Evalメソッドの内部は先に示したコードと同様である。
このように、ファサード・パターンを使用した場合、コンシューマ側のアプリから使いやすい形で既存のライブラリのサービスを提供できるようになる。また、プロキシとは異なり、既存ライブラリが提供するAPIとWindowsランタイム・コンポーネントで公開するAPIが(ほぼ)1対1に対応しなければならないわけでもないため、Windowsランタイム・コンポーネントとしてどんなサービスを提供するかの設計や制約が比較的自由に行える。
ここで、既存のライブラリをWindowsランタイム・コンポーネント化する際に、プロキシとして作成した場合と、ファサードとして作成した場合のそれぞれの長所と短所を以下の表にまとめておこう。
方法 | 長所 | 短所 |
---|---|---|
プロキシ | 機械的に生成可能 元のライブラリの知識があれば利用しやすい |
Windowsストア・アプリの呼び出し粒度にマッチするとは限らない*2 汎用的なライブラリの場合、呼び出し側の学習コストは高い |
ファサード | アプリケーションに提供する機能を制限可能 コンポーネント設計の自由度が高い |
かゆいところに手が届かないAPIセットとなる可能性がある 用途に応じて複数のコンポーネントを提供する必要がある |
既存ライブラリのWindowsランタイム・コンポーネント化にプロキシ/ファサードを使用した場合のそれぞれの長所と短所 |
*2 APIによっては文字列のエンコーディング変換のように呼び出しコストや、中間オブジェクトの生成が無視できない場合がある。また、非同期実行の単位をコンポーネント側で制御できないため、アプリ側に元のライブラリに対するネイティブ・レベルの知識を要求せざるを得ない場合もある。汎用の、つまりアプリ開発者を特定できないWindowsランタイム・コンポーネントを提供するのであれば、これらの点から、ファサード・パターンで実装した専用のWindowsランタイム・コンポーネントを複数用意することが望ましい。
Copyright© Digital Advantage Corp. All Rights Reserved.