SqPlusを使う 第四回

SqPlusを使う 第三回 から間が開いてしまいましたが、前回は静的クラスなどの作り方などについてでしたね。今回は動的クラスについて始めます。

サンプルとしてスクリプト用の2Dベクトルクラスを例として説明していきます。

  サンプルプログラム サイズ:2 MB ダウンロード

sq_vector.h
#pragma once

#ifndef _SQ_VECTOR_H_
#define _SQ_VECTOR_H_

#include <sg/squirrel/UtilSq.h>
#include <sg/math/vector.h>

struct SQVector2 : public SG_VECTOR2
{
public:

	static int constructor(HSQUIRRELVM vm);
	static int release(SQUserPointer up,SQInteger size);

	inline SQVector2& operator  = ( const SG_VECTOR2& rQV)
	{
		x = rQV[0];
		y = rQV[1];
		return *this;
	}
	inline SQVector2 operator+( SQVector2& v) {
		SQVector2 vT;
		vT.x = x + v.x;
		vT.y = y + v.y;
		return vT;
	}
	inline SQVector2 operator-( SQVector2& v) {
		SQVector2 vT;
		vT.x = x - v.x;
		vT.y = y - v.y;
		return vT;
	}
	inline SQVector2 operator- () const{
		SQVector2 vT;
		vT.x = -x;
		vT.y = -y;
		return vT;
	}
	inline float Dot(SQVector2& rV) const{
		return SG_VECTOR2::Dot(rV);
	}
	// 実験用
	int MessageBox(HSQUIRRELVM vm);
	static int StaticTest(HSQUIRRELVM vm);
};
DECLARE_INSTANCE_TYPE(SQVector2)
void SQ_RegistVector2();

#endif // _SQ_VECTOR_H_

sq_vector.cpp
int SQVector2::constructor(HSQUIRRELVM vm)
{
	StackHandler sa(vm);
	SQVector2 *p;
	int paramCount = sa.GetParamCount();
	if (paramCount == 1) {
		p = new SQVector2;
		p->x = p->y = 0.0f;
		return PostConstruct<SQVector2>(vm, p, SQVector2::release);
	} else if (paramCount == 3) {
		p = new SQVector2;
		p->x = sa.GetFloat(2);
		p->y = sa.GetFloat(3);
		return PostConstruct<SQVector2>(vm,p, SQVector2::release);
	} // if
	return sq_throwerror(vm,_T("無効なコンストラクタ引き数"));
}
int SQVector2::release(SQUserPointer up,SQInteger size) {
	if (up) {
		SQVector2 * self = (SQVector2 *)up;
		delete self;
	}
	return 0;
}
int SQVector2::MessageBox(HSQUIRRELVM vm)
{
	StackHandler sa(vm);
	TCHAR t[256]={0};
	_sntprintf_s(t,256, _T("%s:x=%f,y=%f"), sa.GetString(2), x,y);
	::MessageBox(NULL, t, _T("SQVector2::MessageBox()"), MB_OK);
	return 1;
}
int SQVector2::StaticTest(HSQUIRRELVM vm)
{
	TCHAR t[256]={0};
	StackHandler sa(vm);
	SQVector2 *pV2;
	SquirrelObject obj = sa.GetObjectHandle(2);
	pV2 = obj.Get<SQVector2*>();

	_sntprintf_s(t,256, _T("%s:x=%f,y=%f"), sa.GetString(3), pV2->x,pV2->y);
	::MessageBox(NULL, t, _T("SQVector2::StaticTest()"), MB_OK);
	return 1;
}

void SQ_RegistVector2()
{
	SQClassDef<SQVector2>
    (_T("SQVector2")).
      constant(3.1415923f,_T("TEST")).
      funcVarArgs(&SQVector2::MessageBox, _T("MessageBox"), _T("s")).
      staticFuncVarArgs(&SQVector2::StaticTest, _T("StaticTest"), _T("xs")).
      var(&SG_VECTOR2::x,_T("x")).
      var(&SG_VECTOR2::y,_T("y")).
      staticFuncVarArgs(&SQVector2::constructor, _T("constructor"), _T("*")).
      func((void (SQVector2::*)())&SQVector2::Init,_T("Init")).
      func((float(SQVector2::*)() const)&SQVector2::Length,_T("Length")).
      func((float(SQVector2::*)() const)&SQVector2::LengthSq,_T("LengthSq")).
      func((void (SQVector2::*)())&SQVector2::MyRound,_T("MyRound")).
      func((void (SQVector2::*)())&SQVector2::MyFloor,_T("MyFloor")).
      func((void (SQVector2::*)())&SQVector2::Normalize,_T("Normalize")).
      func((void (SQVector2::*)())&SQVector2::Dot,_T("Dot")).
      func((float(SQVector2::*)(SQVector2 &rV) const)&SQVector2::Dot,_T("Dot")).
      func((SQVector2(SQVector2::*)() const)&SQVector2::operator-,_T("_unm")).
      func((SQVector2(SQVector2::*)(SQVector2 &rV))&SQVector2::operator+,_T("_add")).
      func((SQVector2(SQVector2::*)(SQVector2 &rV))&SQVector2::operator-,_T("_sub"));
      }
      </pre>
      <br />
      <strong>no01.nut</strong>
      <pre class="brush: cpp; title: ; notranslate" title="">local v2_01 = SQVector2();
      
      
      print("Test01: x="+v2_01.x+",y="+v2_01.y+"\n");
      
      
      local v2_02 = SQVector2(1.10,2.1);
      print("Test02: x="+v2_02.x+",y="+v2_02.y+"\n");
      
      
      v2_01.x = SQVector2().TEST;
      v2_01.y = v2_01.TEST*2;
      
      local v2_03 = v2_01+v2_02;
      print("Test03: x="+v2_03.x+",y="+v2_03.y+"\n");
      v2_03.MessageBox("Test03");
      
      
      print("Test04: Dot="+v2_03.Dot(v2_01)+"\n");
      
      v2_03.Normalize();
      print("Test05: x="+v2_03.x+",y="+v2_03.y+"\n");
      
      SQVector2.StaticTest(v2_03,"Test05");

いきなりソースを一度に書きましたが、気にせず行きましょう。
とりあえずSqPlusの初期化関係を飛ばし、早速SQVector2について説明していきます。

sq_vector.hにの18行目にあるSG_VECTOR2は、2Dベクトルを便利に扱える構造体です。詳しくはSG_VECTOR2を参照してみてください。
そのベクトルクラスを派生したのが、今回のSQVector2構造体となります。

動的クラスを作成するのに、必ず2つの静的メンバ関数を書きます

 static int constructor(HSQUIRRELVM vm);
static int release(SQUserPointer up,SQInteger size);


これは、スクリプト側で作成&削除時に呼ぶために必要です。

そして、56行目にあるマクロを必ず書きます。括弧の中は、対象とするクラス名を書きます。

DECLARE_INSTANCE_TYPE(クラス名)

これで下準備はとりあえずおしまいです。




sq_vector.cppの説明に入ります。
constructor関数名前通りスクリプト側から呼ばれるコンストラクタですが、no01.nutで言うところの1,7,14行目の所で呼ばれています。
sq_vector.cpp の30行目のrelease関数は、名前の通りSQVector2を解放するときに呼び出される静的メンバ関数であり、constructor関数内で書きます。書き方は、21,26行目にある、

 PostConstruct<対象クラス>(vm, クラスポインタ, 解放するための静的メンバ関数);

これを書けばrelease関数が登録され終わりです。
constructor
関数ですが、引数ももちろん扱えます。17行目に引数の数を取得し、18,22行目で引数の数によって処理が違いますね。

※このとき17行目から取得する引数の数は、1つ多くなる事に注意!

28行目は、それ以外の引数の場合はエラーを出すようにしています。

ちょっと22行目は、数値以外の引数だったらエラーを出すように追加したほうが良いかもしれませんね。やり方は前回のGetTypeを使えばすぐにできます。


release関数の中身を見てみると。。。まぁ、説明するほどでは無いかなw
見たまま、感じ取ってくださいw



さぁ~~~~てきましたよ~~~
58行目のSQ_RegistVector2関数!

60~78行目が混沌としてわけわからない状態に!w
ここは、一つ一つ解説しないとわかりにくいですよねw release関数の説明みたいに感じ取るのは難しいから・・・

まずは60行目のSQClassDefクラス

SQClassDef<対象とするクラス>

まずは、これを書く!これでSqPlus側に自動的に登録されるので簡単ですよね。
次に、61行目にスクリプト側で使うクラス名を書きます。

_T(“SQVector2″)

さて、その後で重要なのが、コンストラクタの登録です。
67行目を見てもらうとあります。

staticFuncVarArgs(&SQVector2::constructor, _T(“constructor”), _T(“*”)).


これを書かないと、スクリプト側からコンストラクタが呼ばれません。2つめの引数 _T("constructor"),も重要なので必ず書いてください。

ん!?staticFuncVarArgsこれは?64行目にも同じものがあるが・・・

こいつは、関数名を直訳すればそのままなのだけども、説明しますw
静的メンバ関数の引数が扱えるバージョンです。引数がいらないやつで

staticFunc

があるけども、これから説明するfuncと同じ書き方なので、まぁ、書かなくてもいいかな。
話は戻りまして、

 ・1つめの引数が対象とする静的メンバ関数ポインタ、
 ・2つめの引数がスクリプト側で使う静的メンバ関数名
 ・3つめの引数が引数の種類:(アスタリスク)で型と引数の数が無制限

となっています。型は前回のList2を参照してください。

説明の順番がちょとおかしいが、次はメンバ変数の登録
65,66行目

 var(&SG_VECTOR2::x,_T(“x”))
 var(&SG_VECTOR2::y,_T(“y”)).


これは、staticFuncVarArgsの説明で直感的にわかるかな、でも一応説明します。

 ・1つめの引数が変数のポインタ、ちなみにSQVector2::xという書き方でもOK
 ・2つめの引数がスクリプト内でのメンバ変数名


お~ぃ、ここまで付いてきてこれているか?と軽く言ってみるテスト

よし、大半が付いてきてきてるみたいだから、話を続けよう。

変数つながりで、62行目をみてみると

 constant(3.1415923f,_T("TEST"))

とある、これは定数を登録するときに使う。

 ・1つめの引数は、数値、文字列が使用可能
 ・2つめの引数は、スクリプト内での定数名

ついにきた!動的メンバ関数、func

 func((void (SQVector2::*)())&SQVector2::Init,_T("Init"))

 ・1つめの引数は、動的メンバ関数のポインタ
 ・2つめの引数は、スクリプト内でのメンバ関数名

おや?76~78行目がちょっと周りのfuncと違がうぞ?
_add_unm_sub って何かというと、メタメソッドです。今回使用したのは3種類だけで、まだ下記のリストのようにまだあります。

(単項)演算子メタメソッド 説明
_add 演算子 +
_sub 演算子 –
_mul 演算子 *
_div 演算子 /
_modulo 演算子 –
_unm 単項演算子 –
_cmp 演算子 < > <= >= をエミュレートするために呼び出される

no01.nutの14行目の所では、_addが呼ばれます。この辺も色々実験してみてください。


 「ん?(void (SQVector2::*)())((SQVector2(SQVector2::*)() const) これはなに?なんでこんなの宣言してるの?」

なんて思っている人は一人もいないと思うので、先に進みたいと思いますw
わからない人は、ためしにその部分を削除してコンパイルしてください。
そして、そのエラーからググって自己解決してください。

ごめんねぇ~この辺の説明はめんどくさいのでw

さて、最後になりました63行目、動的メンバ変数の引数あり版

funcVarArgs(&SQVector2::MessageBox, _T(“MessageBox”), _T(“s”))

・1つめの引数が対象とする動的メンバ関数ポインタ、
・2つめの引数がスクリプト側で使う静的メンバ関数名
・3つめの引数が引数の種類:(アスタリスク)で型と引数の数が無制限

となっています。型は前回のList2を参照してください。
staticFuncVarArgsとほぼ同じ感じだよね。



これで、簡単ながら一通りの説明終わり!




おっと!忘れてた忘れてた^^;
64行目のやつだよ・・・型のやつで"x"とあるが、これはいったい何だ?
と疑問に思う人はほとんどいないと思うけど、念のために説明
こいつは、インスタンスで、早い話スクリプト内で登録されているクラスなどを現します。

no01.nut の24行目の1つめの引数でSQVector2を渡していますよね
これが”x”であり、sq_vector.cpp の45行目SQVector2::StaticTestでスクリプト内での
SQVector2を参照してますね。
参照方法が、49~51行目です。

SquirrelObject obj = sa.GetObjectHandle(2);

まず、これでスクリプト内のSQVector2を取得。でも、このままでは、中身がよくわからない

pV2 = obj.Get<SQVector2*>();

こうすることによって、参照することができるようになる。

どう?簡単でしょ? 間違っても

 delete pV2;

とかしちゃだめだぞ!



まだ、言い忘れていたことがあったよ。引数がある関数は、sa.GetString、sa.GetInt とかを使う場合、前回と同じで、+1してからにしてください。
引数1を参照する場合、sa.GetString(1) でなく sa.GetString(2) だぞ!


さて、今回もスパルタだったかなw
今回の動的クラスは、まだ応用が沢山あるが今日はここまでということで!

予定では次回も動的クラスです。


イメージ1

コメント 

コメントを残す