当前位置: 首页 > 文档资料 > TJS2 参考手册 >

基本的使用方式

优质
小牛编辑
125浏览
2023-12-01

 Borland C++ 5.5 以降 ( C++ Builder 5 以降 ) でコンパイルをすることができます。

 コンパイルには boost.org の regex++ が必要になります。

 regex++ をインストールした後、各 cpp ファイルをコンパイルしてください。

 C++ Builder の場合は、tjs2 の各 cpp ファイルをすべてプロジェクトに追加するだけで OK です。

 gcc 3 以降でもコンパイルできます ( 2.95 でもコンパイルできますが wstring 関連の修正が必要 )。

簡単な例

例:
1|#include<stdio.h>
2|#include"tjs.h"
3|#include"tjsError.h"
4|
5|intmain(intargc,char*argv[])
6|{
7|setlocale(LC_ALL,"");//ロケールを設定
8|
9|tTJS*tjsengine=newtTJS();//TJS2スクリプトエンジンを作成
10|
11|try
12|{
13|tTJSVariantresult;//結果を受け取るための変数
14|
15|tjsengine->ExecScript(
16|TJS_W(
17|"functiontest(x,y){returnx*y;}\n"
18|"returntest(4,5);\n"),
19|&result,NULL,
20|TJS_W("testcode"));//テストスクリプトを実行
21|
22|printf("結果:%d\n",(int)result);//結果を表示
23|}
24|catch(eTJSError&e)
25|{
26|printf("エラーが発生しました:%ls\n",e.GetMessage().c_str());
27|}
28|catch(...)
29|{
30|printf("エラーが発生しました\n");
31|}
32|
33|tjsengine->Shutdown();//TJS2スクリプトエンジンをシャットダウン
34|tjsengine->Release();//TJS2スクリプトエンジンを解放
35|
36|return0;
37|}

2~3行目
TJS2 を使用するのに必要なヘッダファイルを読み込んでいます。tjsError.h は TJS の C++ 例外に関する宣言が含まれています。
7行目
setlocale でロケールを指定しています。ロケールを指定しないと "C" ロケールになるため、日本語文字のナロー文字とワイド文字間の変換がうまくいきません。
9行目
TJS2 スクリプトエンジンを new 演算子で作成しています。
11行目
try ブロックに入っています。TJS2 のエラーは例外により通知されるため、例外処理には慎重になる必要があります。
13行目
 スクリプトを実行した結果を受け取るための tTJSVariant 型の変数を宣言しています。
15~20行目
 tTJS::ExecScript を使ってスクリプトを実行しています。
 第1引数には実行するスクリプトを指定します。tjs_char * 型で渡すために、文字列リテラルを TJS_W マクロを使ってワイド文字列に変換しています。スクリプトでは、関数 test を定義し、その関数を呼んだ結果を返しています。
 この例では return 文により実行の結果を返し、それを result 変数で受け取っていますが、結果を受け取る必要がない場合は return 文も tTJS::ExecScript の2番目の引数も必要ありません ( その場合は2番目の引数は NULL を指定します )。
 tTJS::ExecScript の3番目の引数は実行コンテキストですが、ここでは NULL を指定します。NULL を指定すると スクリプトは global コンテキスト上で実行されます。
 tTJS::ExecScript の4番目の引数は、このスクリプトの名前を指定します。NULLの場合は匿名として扱われます。人間が可読な名前である必要があります。
22行目
 結果を表示しています。ここでは tTJSVariant を int 型にキャストしています。
24行目
 eTJSError 型の例外を受け取っています。
26行目
 eTJSError::GetMessage を使って、例外の理由を表示しています。メッセージを const tjs_char * に変換するために tTJSString::c_str を使っています。tjs_char は ワイド文字のため、printf の変換指定子には %ls を指定しています。
28行目
 その他の例外を受け取っています。
33~34行目
 TJS2 スクリプトエンジンを解放しています。解放に先立ち、tTJS::Shutdown を使って TJS2 スクリプトエンジンをシャットダウンしています。

TJS2側の関数の呼び出し

 TJS2側で宣言した関数をC++から呼び出す方法です。
 前述の try ブロックの中を以下のように書いてみます。

例:
1|tTJSVariantresult;//結果を受け取るための変数
2|
3|tjsengine->ExecScript(
4|TJS_W("functiontest(x,y){returnx*y;}"),NULL,NULL,TJS_W("test"));
5|
6|tjsengine->EvalExpression(TJS_W("test(4,5)"),&result,NULL,NULL);
7|//tTJS::EvalExpressionを使って式を実行
8|
9|printf("結果:%d\n",(int)result);//結果を表示
10|
11|iTJSDispatch2*global=tjsengine->GetGlobalNoAddRef();
12|//グローバルオブジェクトを取得
13|
14|tTJSVariantparam[]={4,5};//パラメータとして渡す変数
15|tTJSVariant*p_param[]={param+0,param+1};//変数へのポインタの配列
16|
17|TJS_THROW_IF_ERROR(global->FuncCall(0,TJS_W("test"),NULL,&result,2,p_param,NULL));
18|//testを関数として呼び出す
19|
20|printf("結果:%d\n",(int)result);//結果を表示

3~4行目
 関数 test を宣言しています。test は global に登録されます。
6行目
 tTJS::EvalExpression を使って式を実行しています。それほど速度的にシビアでなくてもよいならば、このように 式を文字列として渡してその結果を受け取ると楽です。
 ちなみに、単純な式 ( 関数宣言など、他の実行単位を含まないようなもの ) ならば、ある程度、コンパイル結果がキャッシュされ、2回目以降の式評価を高速に行うことができます。
11行目
 グローバルオブジェクトを取得しています。tTJS::GetGlobal と tTJS::GetGlobalNoAddRef の違いは、前者が global オブジェクトの参照カウンタをインクリメントするのに対し、後者はインクリメントしないと言うことです。
 参照カウンタをインクリメントし、使い終わったらデクリメントすると言うことは、その間中、そのオブジェクトが消滅しないようにロックをかけると言うことです。この例のように、global オブジェクトが消滅する心配のない場合は参照カウンタを操作する必要はありませんので tTJS::GetGlobalNoAddRef を使うことができます。また、この場合は使い終わったときの Release は必要ありません。
14~15行目
 関数に渡すパラメータを準備しています。iTJSDispatch::FuncCall は、関数に渡すパラメータとして tTJSVariant 型のポインタの配列を必要とするため、このような準備が必要になります。
17行目
 iTJSDispatch2::FuncCall を使って、関数 "test" を呼び出しています。
 FuncCall の最後の引数は、関数 test に渡される this (実行コンテキスト) ですが、この例で宣言した test 内では this を使っていないので NULL を指定してかまいません。実行すべきコンテキストがある場合は、そのオブジェクトを指定する必要があります。
 TJS_THROW_IF_ERROR は、tjs_error 型の結果がエラーだった場合、それに対応するエラーメッセージとともに例外を送出するマクロです。

ネイティブ関数

 ネイティブ実装 (C++などで実装された関数) を作成し、TJS2 側からそれにアクセスすることができます。
 C++ でなくても、iTJSDispatch2 を実装できる言語ならば、どのような言語で書かれた関数でも呼び出すことができますが、C++ が一番楽でしょう。

 C++ で 関数を書く場合は、tTJSNativeFunction (tjsNative.h に記述) からクラスを導出するのが楽です (しかし、iTJSDispatch2 の FuncCall を実装するだけでも関数として動作はできます)。

 与えられた2つの引数を乗算して返す、簡単な関数を実装してみます。

例:
1|classTestFunc:publictTJSNativeFunction
2|{
3|public:
4|tjs_errorProcess(tTJSVariant*result,tjs_intnumparams,
5|tTJSVariant**param,iTJSDispatch2*objthis)
6|{
7|if(numparams<2)returnTJS_E_BADPARAMCOUNT;//引数が足りない
8|
9|if(!result)returnTJS_S_OK;//結果を格納しなくて良い場合はそのままもどる
10|
11|*result=*param[0]**param[1];//計算
12|
13|returnTJS_S_OK;//正常に終わったことを示すためTJS_S_OKを返す
14|}
15|};

 tTJSNativeFunction を継承したクラスで実装すべきなのは、上記の通り Process メソッドだけです。
 Process メソッドの引数は以下の通りです。

tTJSVariant *result
 関数の結果を格納するための tTJSVariant 型の変数へのポインタが渡されます。呼び出し側が結果を必要としない場合は NULL が渡されますので注意してください。
tjs_int numparams
 関数に渡された引数の数です。
tTJSVariant **param
 関数に渡された引数が格納された tTJSVariant 型の変数へのポインタの配列です。
iTJSDispatch2 *objthis
 関数が実行されるべきコンテキストです。コンテキストに依存しない実装をする場合は無視してかまいません。

 ネイティブ関数は TJS2 からアクセス可能にするため、TJS2 内からアクセスできるオブジェクトに登録する必要があります。以下の例では、global に "test" という名前で登録しています。また、実際にその関数を呼び出しています。

例:
1|iTJSDispatch2*global=tjsengine->GetGlobalNoAddRef();
2|//グローバルオブジェクトを取得
3|
4|iTJSDispatch2*func=newTestFunc();//TestFuncのオブジェクトを作成
5|tTJSVariantfunc_var(func);//tTJSVariant型func_varにオブジェクトを設定
6|func->Release();//funcをRelease
7|
8|TJS_THROW_IF_ERROR(
9|global->PropSet(TJS_MEMBERENSURE,TJS_W("test"),NULL,&func_var,NULL));
10|//登録
11|
12|tTJSVariantresult;//結果を受け取るための変数
13|tjsengine->EvalExpression(TJS_W("test(4,5)"),&result,NULL,NULL);
14|//tTJS::EvalExpressionを使って式を実行
15|
16|printf("結果:%d\n",(int)result);//結果を表示

4~6行目
 ネイティブ関数を実装してあるクラス TestFunc のオブジェクトを作成し、それを tTJSVariant 型に変換しています。
 5行目で tTJSVariant 型に変換していますが、この時点で tTJSVariant 型が 関数オブジェクトの参照カウンタを自動的に管理するので、6行目で関数オブジェクトを Release しています。
8~9行目
 global オブジェクトに関数を "test" という名前で登録しています。global オブジェクトの iTJSDispatch2::PropSet を呼んでいますが、メンバを新規作成させるために TJS_MEMBERENSURE フラグを伴って呼び出しています。
12~16行目
 実際に関数を呼び出し、結果を表示しています。

ネイティブクラス

 TJS2 は C++ 等の言語で書かれたネイティブクラスを扱うための機構を持っています。
 各オブジェクト (iTJSDispatch2 インターフェース) にはネイティブインスタンスと呼ばれる、iTJSNativeInstance 型のオブジェクトを登録することができ、これを オブジェクトから取り出すことができます。
 ネイティブインスタンスは一意なクラス ID で識別され、ネイティブクラスの作成時にはクラス ID を取得する必要があります。

 しかし、これらの操作を行う為のマクロ群が tjsNative.h に定義されているので、これらを利用するのが楽です。
 以下の例は、これらのマクロを使って簡単なクラスを実装するものです。

 まず、ネイティブインスタンスの実装です。ネイティブインスタンスを実装するには tTJSNativeInstance からクラスを導出します。tTJSNativeInstance は tjsNative.cpp / tjsNative.h に実装されているクラスで、iTJSNativeInstance の基本的な動作を実装しています。

例:
1|classNI_Test:publictTJSNativeInstance//ネイティブインスタンス
2|{
3|public:
4|NI_Test()
5|{
6|//コンストラクタ
7|Value=0;
8|}
9|
10|tjs_errorTJS_INTF_METHOD
11|Construct(tjs_intnumparams,tTJSVariant**param,iTJSDispatch2*tjs_obj)
12|{
13|//TJS2オブジェクトが作成されるときに呼ばれる
14|
15|//引数があればそれを初期値としてValueに入れる
16|if(numparams>=1&&param[0]->Type()!=tvtVoid)
17|Value=(tjs_int)*param[0];
18|
19|returnS_OK;
20|}
21|
22|voidTJS_INTF_METHODInvalidate()
23|{
24|//オブジェクトが無効化されるときに呼ばれる
25|}
26|
27|voidSetValue(tjs_intn){Value=n;}
28|tjs_intGetValue()const{returnValue;}
29|
30|tjs_intGetSquare()const{returnValue*Value;}
31|voidAdd(tjs_intn){Value+=n;}
32|voidPrint()const{printf("%d\n",Value);}
33|
34|private:
35|tjs_intValue;//値
36|};

35行目
 話が前後しますが、データメンバです。ネイティブインスタンスには、必要なデータメンバを自由に書くことができます。
4~8行目
 NI_Test のコンストラクタです。C++ クラスとしての初期化は 後述の Construct よりもここで済ませておき、Construct での初期化は最小限の物にすることをおすすめします。
 この例では、データメンバの Value に初期値として 0 を設定しています。
10~20行目
 new 演算子で TJS2 オブジェクトが作成されるときに呼ばれます。numparams と param 引数は new 演算子に渡された引数を表しています。
 tjs_obj 引数は、作成される TJS オブジェクトです。
 この例では、引数があれば (さらにそれが void で無ければ )、それを Value の初期値として設定しています。
22~25行目
 オブジェクトが無効化されるときに呼ばれるメソッドです。ここに終了処理を書くと良いでしょう。
 この例では何もしません。
27~32行目
 データメンバを操作するための公開メソッド群です。後述するネイティブクラス内で、これらを利用するコードを書きます。

 オブジェクトを作成するためにはクラスが必要ですので、クラスを記述します。クラスは tTJSNativeClass を導出する形で実装します。tTJSNativeClass は iTJSDispatch2 インターフェースを持っていて、ネイティブクラスとして振る舞うための基本的な動作が実装されています。
 TJS からアクセス可能なメソッドやプロパティは、ネイティブクラスのコンストラクタ内に記述します。

例:
1|classNC_Test:publictTJSNativeClass//ネイティブクラス
2|{
3|public:
4|NC_Test();//コンストラクタ;下に記述
5|
6|statictjs_uint32ClassID;//クラスID
7|
8|private:
9|tTJSNativeInstance*CreateNativeInstance()
10|{
11|returnnewNI_Test();//ネイティブインスタンスを作成して返す
12|}
13|};
14|tjs_uint32NC_Test::ClassID=(tjs_uint32)-1;//クラスID

4行目
 このクラスのコンストラクタです。実装は後述します。
6行目
 このクラスのクラス ID を保持するための変数です。14行目に実体があります。
9~12行目
 CreateNativeInstance メソッドは、ネイティブインスタンスを作成すべきタイミングで呼ばれるメソッドです。ここでは NI_Test クラスのオブジェクトを作成して返しています。

例:
1|NC_Test::NC_Test():tTJSNativeClass(TJS_W("Test"))
2|{
3|TJS_BEGIN_NATIVE_MEMBERS(/*TJSclassname*/Test)
4|
5|TJS_DECL_EMPTY_FINALIZE_METHOD
6|
7|TJS_BEGIN_NATIVE_CONSTRUCTOR_DECL(
8|/*var.name*/_this,
9|/*var.type*/NI_Test,
10|/*TJSclassname*/Test)
11|{
12|//NI_Test::Constructにも内容を記述できるので
13|//ここでは何もしない
14|returnTJS_S_OK;
15|}
16|TJS_END_NATIVE_CONSTRUCTOR_DECL(/*TJSclassname*/Test)
17|
18|TJS_BEGIN_NATIVE_METHOD_DECL(/*func.name*/print)//printメソッド
19|{
20|TJS_GET_NATIVE_INSTANCE(/*var.name*/_this,
21|/*var.type*/NI_Test);
22|
23|_this->Print();
24|
25|returnTJS_S_OK;
26|}
27|TJS_END_NATIVE_METHOD_DECL(/*func.name*/print)
28|
29|TJS_BEGIN_NATIVE_METHOD_DECL(/*func.name*/add)//addメソッド
30|{
31|TJS_GET_NATIVE_INSTANCE(/*var.name*/_this,
32|/*var.type*/NI_Test);
33|
34|if(numparams<1)returnTJS_E_BADPARAMCOUNT;
35|
36|_this->Add((tjs_int)*param[0]);
37|
38|returnTJS_S_OK;
39|}
40|TJS_END_NATIVE_METHOD_DECL(/*func.name*/add)
41|
42|TJS_BEGIN_NATIVE_PROP_DECL(value)//valueプロパティ
43|{
44|TJS_BEGIN_NATIVE_PROP_GETTER
45|{
46|TJS_GET_NATIVE_INSTANCE(/*var.name*/_this,
47|/*var.type*/NI_Test);
48|*result=_this->GetValue();
49|returnTJS_S_OK;
50|}
51|TJS_END_NATIVE_PROP_GETTER
52|
53|TJS_BEGIN_NATIVE_PROP_SETTER
54|{
55|TJS_GET_NATIVE_INSTANCE(/*var.name*/_this,
56|/*var.type*/NI_Test);
57|_this->SetValue((tjs_int)*param);
58|returnTJS_S_OK;
59|}
60|TJS_END_NATIVE_PROP_SETTER
61|}
62|TJS_END_NATIVE_PROP_DECL(value)
63|
64|TJS_BEGIN_NATIVE_PROP_DECL(square)//square読み出し専用プロパティ
65|{
66|TJS_BEGIN_NATIVE_PROP_GETTER
67|{
68|TJS_GET_NATIVE_INSTANCE(/*var.name*/_this,
69|/*var.type*/NI_Test);
70|
71|*result=_this->GetSquare();
72|
73|returnTJS_S_OK;
74|}
75|TJS_END_NATIVE_PROP_GETTER
76|
77|TJS_DENY_NATIVE_PROP_SETTER
78|}
79|TJS_END_NATIVE_PROP_DECL(square)
80|
81|TJS_END_NATIVE_MEMBERS
82|}

1行目
 NC_Test のコンストラクタです。親クラスである tTJSNativeClass のコンストラクタには TJS2 内で使用するクラス名を指定します。
3行目
 TJS_BEGIN_NATIVE_MEMBERS マクロです。引数には TJS2 内で使用するクラス名を指定します。
 このマクロと TJS_END_NATIVE_MEMBERS マクロで挟まれた場所に、クラスのメンバとなるべきメソッドやプロパティの記述をします。
4行目
 空の finalize メソッドを宣言しています。finalize に相当する処理は tTJSNativeInstance::Invalidate をオーバーライドすることでも実装できますので、通常は空のメソッドで十分です。
7~16行目
 (TJSの) コンストラクタを宣言しています。TJS でクラスを書くとき、クラス内でクラスと同名のメソッドを宣言している部分に相当します。

 TJS_BEGIN_NATIVE_CONSTRUCTOR_DECL マクロの1番目の引数はネイティブインスタンスに割り当てる変数名で、2場面目の引数はその変数の型名です。この例でのこのブロック内では NI_Test * _this という変数が利用可能で、ネイティブインスタンスにアクセスすることができます。
 マクロの3番目の引数は、TJS 内で使用するクラス名を指定します。TJS_END_NATIVE_CONSTRUCTOR_DECL マクロの引数も同様です。
 ここも、コンストラクタに相当する処理は tTJSNativeInstance::Construct をオーバーライドする事で実装できるので、ここでは何もせずに S_OK を返します。

18~27行目
 print メソッドを宣言しています。メソッド名は TJS_BEGIN_NATIVE_METHOD_DECL と TJS_END_NATIVE_METHOD_DECL の両マクロに同じものを指定する必要があります。
 このマクロ内で使用可能な変数に tjs_int numparams と tTJSVariant **param があって、それぞれ、渡された引数の数と引数を示しています。このメソッドではそれらは使用していません。
 20~21行目は、オブジェクトからネイティブインスタンスを取り出すためのマクロです。この例では _this という NI_Test * 型の変数にネイティブインスタンスを取り出す、という意味になります。以降、_this という変数でネイティブインスタンスにアクセスできます。23行目で、そのネイティブインスタンスの Print メソッドを呼び出しています。
29~40行目
 add メソッドを宣言しています。ここでは numparams と param を使用しています。
42~62行目
 value プロパティを宣言しています。TJS_BEGIN_NATIVE_PROP_DECL と TJS_END_NATIVE_PROP_DECL の両マクロには、メソッドの宣言と同じく、プロパティ名を指定します。

 TJS_BEGIN_NATIVE_PROP_GETTER と TJS_END_NATIVE_PROP_GETTER マクロで囲まれた場所には、ゲッターを記述することができます。ゲッター内では tTJSVariant 型である *result に値を設定するように記述します。
 同様に、TJS_BEGIN_NATIVE_PROP_SETTER と TJS_END_NATIVE_PROP_SETTER マクロで囲まれた場所にはセッターを記述することができます。セッター内では tTJSVariant 型である *param に設定されるべき値が格納されているので、それを使って処理をします。

64~79行目
 ここでは読み出し専用プロパティを宣言しています。セッターの代わりに TJS_DENY_NATIVE_PROP_SETTER を書くことにより、読み出し専用プロパティを作ることができます。

 ネイティブクラスの登録は、ネイティブ関数の登録と同じです。以下にテストコードを例示します。

例:
1|iTJSDispatch2*global=tjsengine->GetGlobalNoAddRef();
2|//グローバルオブジェクトを取得
3|
4|iTJSDispatch2*cls=newNC_Test();//NC_Testのオブジェクトを作成
5|tTJSVariantcls_var(cls);//tTJSVariant型cls_varにオブジェクトを設定
6|cls->Release();//clsをRelease
7|
8|TJS_THROW_IF_ERROR(
9|global->PropSet(TJS_MEMBERENSURE,TJS_W("Test"),NULL,&cls_var,NULL));
10|//登録
11|
12|tjsengine->ExecScript(TJS_W(
13|"vartest=newTest();\n"
14|"test.value=5;\n"
15|"vartest2=newTest(test.square);\n"
16|"test2.add(3);\n"
17|"test2.print();\n\0"),
18|NULL,NULL,NULL);//スクリプトを実行