SqPlusを使う 第三回

前回(SqPlusを使う 2回目)でスクリプトから関数を呼出、変数の変更までをやりました。
今回は、プログラム側で定義済み変数、静的クラス、グローバル関数を作り、スクリプト側から呼び出す処理をします。

ディレクトリ構造は前回と同じような感じなので、今回は省かせてもらいます。

サンプルプログラム サイズ:2 MB ダウンロード
#include <stdafx.h>

//--------------------------------------------
// テスト用フラグ
_DECL_STATIC_NAMESPACE(TEST)
//-------------------------
_BEGIN_NAMESPACE(TEST)
_BEGIN_NAMESPACE_CONSTANTS(TEST)
_CONSTANT(TEST_INT,		OT_INTEGER,	1)
_CONSTANT(TEST_FLOAT,	OT_FLOAT,	3.14f)
_CONSTANT(TEST_STRING,	OT_STRING,	_T("test"))
_END_NAMESPACE(TEST, NULL)

//--------------------------------------------
// 数学
#define PI_D180 0.0174532925199f
// Degrees sin
_MEMBER_FUNCTION_IMPL(MATH,DSin)
{
	StackHandler sa(v);
	return sa.Return(sinf(sa.GetFloat(2)*PI_D180));
}
// Degrees cos
_MEMBER_FUNCTION_IMPL(MATH,DCos)
{
	StackHandler sa(v);
	return sa.Return(cosf(sa.GetFloat(2)*PI_D180));
}
// Degrees tan
_MEMBER_FUNCTION_IMPL(MATH,DTan)
{
	StackHandler sa(v);
	return sa.Return(tanf(sa.GetFloat(2)*PI_D180));
}

_DECL_STATIC_NAMESPACE(MATH);
//-------------------------
_BEGIN_NAMESPACE(MATH)
_MEMBER_FUNCTION(MATH,DSin, 2,_T(".n"))
_MEMBER_FUNCTION(MATH,DCos, 2,_T(".n"))
_MEMBER_FUNCTION(MATH,DTan, 2,_T(".n"))
_BEGIN_NAMESPACE_CONSTANTS(MATH)
_CONSTANT(PI,		OT_FLOAT, 3.1415926f)
_CONSTANT(PI2,		OT_FLOAT, 6.283185307179586232f)
_CONSTANT(HALF_PI,	OT_FLOAT, 1.570796326794896619f)
_CONSTANT(RADIAN,	OT_FLOAT, 57.29577951308232088f)
_CONSTANT(PI_D180,	OT_FLOAT, 0.017453292519943296f)
_END_NAMESPACE(MATH,NULL)

//
int CallTest01(HSQUIRRELVM v)
{
	StackHandler sa(v);

	printf("CallTest01が呼ばれたよ");
	return SQ_OK;
}
int CallTest02(HSQUIRRELVM v)
{
	StackHandler sa(v);
	SQChar szTmp[256],seRet[256]={0};

	_tcscat(seRet, _T("CallTest02("));
	int max = sa.GetParamCount()+1;
	for(int i=2;i&lt;max;++i )
	{
		switch(sa.GetType(i))
		{
			case OT_INTEGER:
				scsprintf(szTmp, _T("int=%d,"), sa.GetInt(i));
				break;
			case OT_STRING:
				scsprintf(szTmp, _T("string=%s,"), sa.GetString(i));
				break;
			case OT_FLOAT:
				scsprintf(szTmp, _T("float=%f,"), sa.GetFloat(i));
				break;
			case OT_BOOL:
				scsprintf(szTmp, _T("bool=%d,"), sa.GetBool(i));
				break;
			default:
				scsprintf(szTmp, _T("不明,"));
		}
		_tcscat(seRet, szTmp);
	}
	_tcscat(seRet, _T(")"));

	return sa.Return(seRet);
}



int _tmain(int argc, _TCHAR* argv[])
{
	//-------------------
	// 初期設定
	//-------------------
	SquirrelVM::Init();
	HSQUIRRELVM v = SquirrelVM::GetVMPtr();
	sq_setprintfunc(v, SQErrMsg);
	SQ_StdSetErrorHandlers(v);
	SquirrelObject root = SquirrelVM::GetRootTable();
	_tsetlocale(LC_ALL, _T(""));

	// 定義済み
	BindConstant(2.7182818f, _T("TEST_EX") );
	BindConstant(_T("テスト文字列"), _T("TEST_STRING") );
	// 
	int iVar = 999;
	BindVariable(root,&amp;iVar,_T("g_iVar"));

	ScriptStringVar128 testString;
	scsprintf(testString,_T("テスト文字列ですが何か?"));
	BindVariable(root,&amp;testString,_T("g_testString"));
	// 登録
	_INIT_STATIC_NAMESPACE(TEST);
	// 静的数学クラス登録
	_INIT_STATIC_NAMESPACE(MATH);
	// グロバール関数作成
	SquirrelVM::CreateFunctionGlobal(CallTest01,_T("CallTest01"),_T("i"));
	SquirrelVM::CreateFunctionGlobal(CallTest02,_T("CallTest02"),_T("..."));

	//-------------------
	// ファイル読み込み
	//-------------------
	HRESULT hr;
	TCHAR szPath[MAX_PATH]={0};
	// ファイルサーチからの名前から検索
	if( FAILED( hr = FindFileCchEx( szPath, MAX_PATH, SCRIPT_DIR, _T("no01.nut"))) ){
		MessageBox(NULL,_T("no1.nutファイル読み込み失敗"),_T("_tmain()"),MB_OK);
		SquirrelVM::Shutdown();
		return 0;
	}

	try {
		SquirrelObject main = SquirrelVM::CompileScript(szPath);
		SquirrelVM::RunScript(main);

	}catch(SquirrelError &amp;e) {
		MessageBox(NULL,e.desc,_T("_tmain()"),MB_OK);
		SquirrelVM::Shutdown();
		return 0;
	}

	SquirrelVM::Shutdown();

	MessageBox(NULL,_T("終了します"),_T("終了"),MB_OK);
	return 0;
}
source1

早速source1のソースを見てください。前回と違い見慣れない関数やマクロなどが出てます。初期設定は前回やったので省きます。

では最初にスクリプト内で定義済み変数を使う方法ですが、106、107行目を見てください。BindConstant関数を使って定義をしています。

引数1が文字列と数値のみが使用可能です
引数2が変数名を表します。

どう?かんたんでしょ?
これでスクリプト内で定義済み変数が使い放題です。







プログラム側の変数をスクリプト側で参照し、自由に値を変更する方法があります。
109行目に変数iVarを110行目のBindVariable関数を使用することにより、スクリプト側で、iVarの値を自由に変えることができる。

引数1が、SquirrelObjectの値
引数2が、数値、文字列、ユーザーポインタ、インスタンス、などが使用可能です。
引数3が、スクリプト側での変数名

114行目は、変数を文字列に変えただけです。色々と試してみてください。


静的クラスはちょっとややこしいですが、今から説明するマクロの使い方さえ覚えれば直ぐにマスターできます。
1つめの静的クラスのタイプは、静的メンバ関数が無いものです。5行目から12行目がそれにあたります。5行目の_DECL_STATIC_NAMESPACE(TEST)が、静的クラスを作る初めのマクロです。括弧に入る文字列が、スクリプト側での静的クラス名です。
7行目の_BEGIN_NAMESPACE(TEST)は、次に静的メンバ関数を宣言するのに使用するけど、ここでは使用しませんが、必ず書いてください。括弧の文字列は、同じ名前にしてください。

8行目の_BEGIN_NAMESPACE_CONSTANTS(TEST)は、静的メンバ変数宣言を次の行から書くために必要です。括弧の文字列は同じ名前です。

9行目からの_CONSTANT()は、1つめのパラメーターは、静的メンバ変数のスクリプト側で使用する変数名です。2つ目のパラメーターが変数の型を表します。種類は、OT_INTEGEROT_FLOATOT_STRINGこの3つのみです。※List1参照

12行目の、_END_NAMESPACE(TEST, NULL)は、終了時に書きます。1つめのパラメーターは、静的クラス名です。2つめのパラメーターは、NULLを指定してください。

これで、静的メンバ関数が無い静的クラスを構築ができます。

静的メンバ関数をもたせているのが、36行目から48行目のやつです。先ほどと違うのが、_BEGIN_NAMESPACEの下に_MEMBER_FUNCTIONが書かれていることです。では早速説明を!
1つめのパラメーターは、静的クラスの名前です。2つめは、スクリプ側で使用する関数名かつ、プログラム側での関数名でもあります。3つめは、引数の数で、必ず使用する引数の数+1してください。4つめは、引数の型を表す文字列で、最初に必ず”.”を入力し、引数の数だけList2にある文字を追加します。

2つめでの関数名ですが、プログラム側では_MEMBER_FUNCTION_IMPLマクロを使用して関数を作ります。1つめのパラメーターが静的クラスの名前で、2つめがスクリプト側で使用する(_MEMBER_FUNCTIONの2つめのパラメータで使用)名前になります。

さて、これらTEST,MATHの静的クラスをスクリプト側で使用するには、まだ一手間あります。116、118行目に_INIT_STATIC_NAMESPACEのマクロを書き、括弧の中に、それぞれの静的クラス名をかいて終わりです。これでスクリプト側から使用可能となります。








今回最後となる、通常の関数(グローバル関数)についてです。その部分は120、121行目にあるSquirrelVM::CreateFunctionGlobalのメンバ関数です。使い方は簡単です。1つめの引数にプログラム側で作った関数。2つめの引数にスクリプト側での関数名。3つめの引数に引数の型を表す文字列で、List2を参照。

2つめの引数にある関数の作り方ですが、まず戻り値はintであること、そして引数は必ずHSQUIRRELVMであることです。

int 関数名(HSQUIRRELVM v)

といった感じになります。sorce1にあるCallTest01関数ですが、引数が1つの整数となっていますが、関数無いではただ単にprintf(“CallTest01が呼ばれたよ”);となってます。次の関数CallTest02を参考にスクリプト側からの引数を表示するように修正してみてください。ちなみに、戻り値がない場合はSQ_OKを戻してください。

CallTest02は、なにやら少しゴチャゴチャしてますね。まずは、最小のStackHandler sa(v);ですが、これはスクリプト側からの引数や、スクリプト側への戻り値などを簡単に扱うために使うクラスです。必ず最初に必要です。

64行目のGetParamCountは、この関数の引数の数を表します。

67行目のGetTypeは、GetType(idx)のidxの値によって、してした場所の引数の型を取得することができます。型はList1を参照。ちなみに引数の一番初めの値は2からとなります。

67行目のswitchですが、OT_INTEGERにはsa.GetInt(i)OT_STRINGにはsa.GetString(i)OT_FLOATにはsa.GetFloat(i)OT_BOOLにはsa.GetBool(i)、となります。他にもList1を見てもらうと分かるように、型の定義済み変数がありますが、今回は割愛させてもらいます。GetTypeと同じく、i の始まる値は2からとなります。

88行目のスクリプト側への戻り値がsa.Returnになります。これは、それぞれの型が用意されているので、特に注意をする必要はないです。

引数が必要がない場合は、下記のような書き方でできます。

 SquirrelVM::CreateFunctionGlobal(関数名, _T(“スクリプト側での関数名”))

引数3を省略するだけです。

ちなみにimage1が今回実行したときの結果となります。スクリプトファイルno1.nutを参照してくださいね。

squirrel定義済み変数
OT_NULL null
OT_INTEGER integer
OT_FLOAT float
OT_BOOL bool
OT_STRING string
OT_TABLE table
OT_ARRAY array
OT_USERDATA userdata
OT_CLOSURE closure
OT_NATIVECLOSURE nativeclosure
OT_GENERATOR generator
OT_USERPOINTER userpointer
OT_THREAD thread
OT_FUNCPROTO functiuon prot
OT_CLASS class
OT_INSTANCE instance
List1


文字型
o null
i integer
f float
n integer, float
s string
t table
a array
u userdata
c closure, nativeclosure
g generator
p userpointer
v thread
x instance
y class
b bool
. any
List2

今回も、おおざっぱで駆け足でしたが、だいたいの使い方が分かってもらえたでしょうか?今後は割愛したところを応用編で話したいとは思ってます(未定)。

次回は、一番重要な所!動的クラスを書きたいと思います。


image1

 次回へ続く

コメント 

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください