SqPlusを使う 第二回

前回(SqPlusを使う 1回目)はものすごい駆け足で話を進めてしまいましたね^^;
毎回こんな感じで進むと思います。

まずはじめに、サンプルの構成について説明をします。
前回ディレクトリ構造をさらに説明します。今回のサンプルを解凍すると以下のような感じのディレクトリ構造になります。

sp001
┣ bin(ビルド後の実行ファイルが作られる)
┃  ┗ data(画像、音声、スクリプトなどがある)
┃    ┗ sc(スクリプトがある)
┣ lib(ビルド後にSGLibプロジェクトのライブラリが作られる)
┣ obj(ビルドしたときに中間ディレクトリとして使われる)
┗ source(プログラム”VS2010″で作られたプロジェクトがある)
  ┣ 3rdParty
  ┃ ┣ inc(squirrel、SqPlusヘッダファイル)
  ┃ ┗ libs_x86(squirrel、SqPlusのデバッグ、リソース版のライブラリ)
  ┣ No1
  ┗ SGLib(プログラムを扱いやすくするためのライブラリ、主にsquirrel関係)

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

このサンプルプログラムは、squirrelの2.2.4のバージョンでライブラリファイルが作られています。格納場所は3rdParty内のinclibx_x86それぞれ入っています。
libx_x86内のライブラリ名は、純正のSqPlusでコンパイルしたときのライブラリ名と違い、全てUNICODE文字セット有効でコンパイルして”U”という頭文字はつきません。後デバッグ版のライブラリは、全て"_D"という頭文字が追加さています。確認してみてください。

さて、VS2010でプロジェクトNo1を選び、下記のソース(No01.cpp)を見てください。
// No01.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"

#include <sg/util/util.h>
#include <sg/squirrel/UtilSq.h>
using namespace SqPlus;

#define SCRIPT_DIR _T("./data/sc")

#if defined(DEBUG) || defined(_DEBUG)
	#pragma comment( lib, "SGLib_D.lib" )
	#pragma comment( lib, "sqdbglib_D.lib" )
	#pragma comment( lib, "sqplus_D.lib" )
	#pragma comment( lib, "sqstdlib_D.lib" )
	#pragma comment( lib, "squirrel_D.lib" )
#else
	#pragma comment( lib, "SGLib.lib" )
	#pragma comment( lib, "sqdbglib.lib" )
	#pragma comment( lib, "sqplus.lib" )
	#pragma comment( lib, "sqstdlib.lib" )
	#pragma comment( lib, "squirrel.lib" )
#endif


int _tmain(int argc, _TCHAR* argv[])
{
	//-------------------
	// 初期設定
	//-------------------
	SquirrelVM::Init();
	HSQUIRRELVM v = SquirrelVM::GetVMPtr();
	sq_setprintfunc(v, SQErrMsg);
	SQ_StdSetErrorHandlers(v);
	_tsetlocale(LC_ALL, _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);
		return 0;
	}
	
	try {
		SquirrelObject main = SquirrelVM::CompileScript(szPath);
		SquirrelVM::RunScript(main);
		SquirrelObject root = SquirrelVM::GetRootTable();

		// no01.nutからCallTest関数を呼ぶ
		SquirrelFunction<float> callFunc(_T("CallTest"));
		float pi = callFunc(_T("_tmain()より呼び出しました。"));
		_tprintf(_T("CallTestの戻り値 = %f\n"), pi);

		// 変数の値を変える
		root.SetValue(_T("nTest"), 1234);
		int num = root.GetInt(_T("nTest"));
		_tprintf(_T("変数nTest = %d\n"), num);

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

	SquirrelVM::Shutdown();

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

No01.cpp ファイルの7行目のインクルードは SqPlus ライブラリを使用できるようにするためのものです。これはすでにSGLibの領域ですが、sqplus.hのインクルードが含まれてます。

これでとりあえずSqPlusが使用可能になったところで、32行目を見てみると、ここでSqPlusの初期化を開始します。
次に33行目はVM(仮想マシン)を次の2つの関数で必要なため取得しています。
34行目の sq_setprintfunc(v, SQErrMsg); は、スクリプトの”print”をSGLibで作られたビュアーで表示去るために登録してます。
35行目も同様にSQ_StdSetErrorHandlers(v); は、詳細なエラー表示をビュアーに表示させるためのものです。この関数はSGLibで作ったものです。関数を右クリックして、ポップアップメニューの定義域へ移動で確かめてみてください。
これでとりあえず、初期化は終わりです。

40~46行目は、ディレクトリのsp001\bin\data\sc\内のno01.nutスクリプトファイルのファイルパスを作るところです。ファイルが存在しない場合はエラーを出し終了させています。

そして、やっとここでファイル読み込みをしコンパイルするところが49行目に当たります。そのコンパイルしたオブジェクトを実行させるところが50行目になります。
ここで何らかのエラーが発生した場合は、trycatchで挟まれているので、64行目へ行き終了です。

54行目はスクリプトファイル側の関数を呼び出すためのものです。 SquirrelFunction<float> callFunc(_T("CallTest"));
下記のno01.nutファイルの26行目の所ですね。
local nTest = 10;

//
// スクリプト内から呼び出すためのテスト関数
//
function Test(str)
{
	local i;
	local aX = [];
	aX.resize(5);
	
	for(i=0;i&lt;5;i++)
	{
		aX[i] = i*3.141592;
		print("No."+i + ": "+str+"\n");
	}	
	for(i=0;i&lt;5;i++)
	{
		print("aX[" + i + "]" + " = " + aX[i]+"\n");
	}	
}

//
// プログラム側から呼び出すためのテスト関数
//
function CallTest(str)
{
	print("関数 CallTest(\"" + str + "\")"+"\n");
	return 3.141592;
}


// スクリプト内から呼び出す
Test("No.01 test");
print("nTest = " + nTest + "\n");

ここでSquirrelFunctionのことについて説明を!
このクラスはスクリプト内の関数を呼び出すものですが、ここでは”<>”の中がfloat型です。下の動作させたときの図1を見てもらうと、CallTestの戻り値が3.141592とでは、もし”<>”の中身をint型にした場合は、3になります。

 では文字列の場合は?

これは、"const SQChar*"でなければなりません。ちなみにSQCharは、UNICODEで定義されているのでwchar型となっています。

SquirrelFunction<const SQChar*> 
callFunc(_T("CallTest"));
  const SQChar* pi = callFunc(_T("_tmain()より呼び出しました。"));
  _tprintf(_T"CallTestの戻り値 = %s\n"), pi);

この状態でスクリプト側のCallTestを呼び出すと型が一致しないエラーがでるので、 スクリプト側の29行目を

 return ""+3.141592;

とすることで回避できます。
もし戻り値がない場合は、"<>"の中身をvoidとすれば大丈夫です。他にも、オブジェクト(クラス)を返すことも可能です。

SquirrelFunction<hoge &>callFunc・・・

といったかんじに・・・この辺は次回以降扱います。

次はスクリプト側への関数の引数ですが、ソースの55行目を見てください。文字列(”_tmain()より呼び出しました。”)を渡していますね。図1にもちゃんと表示されています。この引数ですが、自動的に型変換がされるため、float、int、任意のオブジェクトなども引数として使用することができます。ちなみに引数の最大数は7個までです。
任意のオブジェクトを引数とする方法は、次回以降扱とします。

さて、スクリプト側の関数呼び出し、引数の使い方については慣れたかな?


最後にスクリプトの1行目nTest変数の変更の仕方です。
ソースの51行目でまずはルートテーブルを取得し、59行目でスクリプト側の変数nTestに1234という数値をセットし、60行目で取得し直してます。ちょっとややこしい方法ですね。図1の結果”変数nTest = 1234”で確認できます。

SqPlusを終了する場合は、SquirrelVM::Shutdown();を呼び出して終わりです。

SqPlusの基礎部分を触れてみましたがどうだったでしょうか?

では、今回はここまで!

 次回へ続く (SqPlusを使う 第三回)

コメント 

コメントを残す