SqPlusでオブジェクトを読み書き

Last-modified: 2008-10-14 (火) 11:07:15

(このページは『Sq Plus Get Set Objects』の和訳です。)

要約: あなたは今までに、SquirrelオブジェクトからC++へとテーブルを使わずにダイレクトにアクセスしたいと思ったことはありませんか?

私は以下のような種類のシチュエーションからこの動機を得ました。
このような機能を持つSquirrelスクリプトを想像してみよう。

Vector3 doSomething(Vector3){...}

C++の中でこの関数を呼び出したければ、これのように記述するでしょう。

// 'returnFunction' 関数のリファレンスを取得
SqPlus::SquirrelFunction doSomething(scriptNamespace, "doSomething");
// Vector3オブジェクトである(だろう)戻り値を手に入れる
SquirrelObject returnObj = scriptFunction(Vector3(1,2,3));

今あなたがC++側で書いている時、どのようにreturnObjからVector3を取り出しますか?
さて、私にはあなたの声が聞こえます。「なぜ(戻り値を強制した)テンプレートメソッドを使うだけじゃダメなんだろう?」
それはVector3*かVector3型を返し、全ての問題を解決してくれるでしょう。
そして完全に有効です。もしあなたが戻り値が何かを知っていれば。
もしそうでないならば、戻り値の型が何であるかをチェックする方法がまず必要になります。そして値を手に入れる良い方法もです。
幸いにも、特別の型に対してテストするための能力がSqPlusリビジョン17で追加されました。
ここに、どのようにあなたがSquirrelObjectから型に変換し、戻すことができるかを示します。(型が一致しない場合は例外を投げる)

// SquirrelObjectから何らかの境界型を得る。(注意:Squirrelがハンドルしているリファレンスやポインターはまだ保持されている)
template<class _ty> static _ty Get(SquirrelObject& obj) {
    sq_pushobject(globalVM.GetVMHandle(),obj.GetObjectHandle());
    _ty val = SqPlus::Get(SqPlus::TypeWrapper<_ty>(),globalVM.GetVMHandle(),-1);
    sq_pop(globalVM.GetVMHandle(),1);
    return val;
}
// 何らかの型をSquirrelObjectにセットする。(注意:Squirrelがハンドルしているリファレンスやポインターはまだ保持されている)
template<class _ty> static SquirrelObject Set(_ty val) {
    SquirrelObject obj(globalVM.GetVMHandle());
    SqPlus::Push(globalVM.GetVMHandle(), val);
    obj.AttachToStackObject(-1);
    sq_pop(globalVM.GetVMHandle(),1);
    return obj;
}

このfunctionは、別の変更の中で提案されるように、VMがオブジェクトとして扱われると仮定します。(訳者注:???)
事実上、どんな場所でもglobalVM.GetVMHandle()を実行すればSquirrelObjectが属するVMを得ることができます。これでどんなメソッドも呼び出せるでしょう。
これがstatic関数なら、SquirrelObjectのメンバ変数にすることもできるかもしれません。そしてこれは典型的な変更です。なぜならユーザーはこのような使い方をするからです。

Vector3 v(1,2,3);
SquirrelObject t; // VMから生成する。静的なnamespaceまたはVMオブジェクトを使う。
t.Set(&v);
// または、単にこのようにも記述できる: SquirrelObject t(Vector3(1,2,3));
// そしてそれはずっとクールだ :-)
// 「returnFunction」関数のリファレンスを取得する
SqPlus::SquirrelFunction doSomething(scriptNamespace, "doSomething");
// 実行して、Vector3型であろう戻り値を取得する。
SquirrelObject returnObj = scriptFunction(t);
Vector3* v_out( returnObj.Get<Vector3*>() );

ここのキーは、機能性は正確に同じであるということです。しかし、scriptFunctionの記述はよりgenericです。
これは異なる記述をしたSquirrelFunctionを含むC++ STLコンテナを許容する利点がある。従ってこんな感じで:

std::map<std::string,SqPlus::SquirrelFunction> callBacks;

callBacksがどんな異なる型を返しても可能です。
事実上、この変更によりSquirrelObjectはBoost:Anyのようにいかなる型の振る舞いもできるようになります。
これらの機能はtemplateで実現されるので、SqPlusの残りへのインパクトは最小になります。

コンパイラに関する私の知識に間違いがなければ、使用されない場合はコンパイルされず、コードの肥大化も最小になります。

リリース24で追記された:

// From testSqPlus2.cpp:
void testSquirrelObjectSetGet(void) {
  float val = 1.234f;
  SquirrelObject t(val);
  float val2 = t.Get<float>();
  scprintf(_T("Val2 is: %f\n"),val2);
  if (1) {
#if 0
    SquirrelObject v(Vector3(1.f,2.f,3.f)); // 値渡し:(注:何回オブジェクトが生成・破壊されたか)
#else
    SquirrelObject v(Vector3(1.f,2.f,3.f),true); // コピーの代わりとしてリファレンス渡し:一時オブジェクトとしてVector3() は使用され、生成、解放される。
#endif
    Vector3 * pv = v.Get<Vector3 *>();
    scprintf(_T("Vector3 is: %f %f %f\n"),pv->x,pv->y,pv->z);
    pv->z += 1.f;
    if (1) {
      SquirrelObject v2p(pv); // This is a pointer to v's instance. It will not get freed when v2p goes out of scope.
      pv = v2p.Get<Vector3 *>();
      scprintf(_T("Vector3 is: %f %f %f\n"),pv->x,pv->y,pv->z);
    } // if
  } // if
  scprintf(_T("Vector3() instance has been released.\n\n"));
} // testSquirrelObjectSetGet

ツッコミ歓迎