スクリプトには、命令コマンドとパラメータ(式)の2つの部分があります。
命令として現れるコマンドには cmdfunc が呼ばれるのは前回までに何度もみました。
一方、パラメータ(式)の中で現れるコマンドに対しては reffunc という関数が使用されます。
reffunc は、関数とシステム変数のキーワードを処理する関数です。
その扱いは cmdfunc と非常に似ていますが、微妙に違う点もあるので、解説します。
今回のソースコードはここにあります:HSP3向けプラグイン「varinfo」リポジトリ
GetVarinfo命令では、いちいち配列変数を使うので、結構面倒でした。
今回作る関数 varinfo() は、vartype() などのように関数形式です。
式の中で使用できる方が、使いやすくて良いですよね。
今までの cmdfunc() 関数は、簡単に言うと code_next() と switch だけでした。
最後に RUNMODE_RUN を返していますが、これは気にしなくてもいいです。
reffunc() 関数では、引数を囲む丸括弧 ( ) の処理が必要なので、少々長めになります。
※今回はシステム変数は使いませんが、参考のためシステム変数を扱う部分をコメントで表示しています。
引数の cmd は、cmdfunc() のときと同じように、コマンドの code 値です。この値を元に、switch で処理を分岐します。
switch に相当する処理は、ProcFunc, ProcSysvar という別の関数に分けることにしましょう。そのほうがみやすいです。
もう一つの引数 type_res は、返値の型タイプ値をもつ変数へのポインタです (これはHSP側が用意しています)。これに間接参照して、返値の型を設定しておきます。( 設定しなければエラー )
ProcFunc() や ProcSysvar() の返値を、*type_res = ...(); で受け取っていますね。
reffunc は関数やシステム変数を処理した結果の「値」を返すので、「型タイプ値」と「ポインタ」という2つのデータを返却する必要があります。
そのため関数の返値としてポインタを、type_res が指す先の領域に型を書き込む、という手段を用いているわけです。
しかし ProcFunc, ProcSysvar では逆にしています。返値として「型タイプ値」を返し、ppResult という引数が指す領域に「ポインタ」を書き込む、というように。
これはたぶん、こっちのほうがコードが書きやすいと思うためです。僕の好みかもしれませんけれど。
if文の条件式で、type や val というグローバル変数に間接参照しています。(グローバル変数にあるまじき名前だというのは我慢しましょう……。)
type, val はそれぞれ、「次にあるトークン」の type, code 値へのポインタです。
トークンというのは、変数やコマンドの名前、数値リテラル、1つの記号など、スクリプトの中の単語のようなものです。
変数「type」が指す type 値はプラグインの番号だと以前の回にいいましたが、もう少し詳しくいいましょう。
その値は TYPE_... という定数の値か、それ以上です。
// command type #define TYPE_MARK 0 // 記号(code=文字コード) #define TYPE_VAR 1 // ユーザー定義変数(code=変数ID) #define TYPE_STRING 2 // 文字列(code=DSオフセット) #define TYPE_DNUM 3 // 実数値(code=DSオフセット) #define TYPE_INUM 4 // 整数値(code=値) #define TYPE_STRUCT 5 // モジュール変数・構造体(code=minfoID) #define TYPE_XLABEL 6 // 未使用 #define TYPE_LABEL 7 // ラベル名(code=OTオフセット) #define TYPE_INTCMD 8 // HSP内部(コア)命令(code=コマンドID) #define TYPE_EXTCMD 9 // HSP拡張(機種依存)命令(code=コマンドID) #define TYPE_EXTSYSVAR 10 // HSP拡張(機種依存)システム変数(code=コマンドID) #define TYPE_CMPCMD 11 // 比較命令(code=コマンドID) #define TYPE_MODCMD 12 // ユーザー拡張命令・関数(code=コマンドID) #define TYPE_INTFUNC 13 // HSP内部(コア)関数(code=コマンドID) #define TYPE_SYSVAR 14 // HSP内部(コア)システム変数(code=コマンドID) #define TYPE_PROGCMD 15 // プログラム制御命令(code=コマンドID) #define TYPE_DLLFUNC 16 // 外部DLL拡張命令・関数(code=コマンドID) #define TYPE_DLLCTRL 17 // 拡張DLLコントロールコマンド(code=コマンドID) #define TYPE_USERDEF 18 // HSP3拡張プラグインコマンド(code=コマンドID) #define TYPE_ERROR (-1) // #define TYPE_CALCERROR (-2) //
引用元はhsp3struct.h。TYPE_XLABEL はコンパイル中にのみ使用されるので、プラグイン側が読み取ることは絶対にありません。
TYPE_MARK から TYPE_LABEL までは、スクリプトに書かれた定数のタイプです。
例えば、スクリプトに「123」と書けば、その部分は TYPE_INUM になります。その code 値は 123。
TYPE_INTCMD 以降は、標準のコマンドです。TYPE_INTCMD に属するコマンドが書いてある場合は、type が TYPE_INTCMD になり、val がそれの code 値になります。
code_**() 系の命令を使うと、HSPがスクリプトの続きを読み、これが更新されます。
※標準命令のコマンドは定義済みです。TYPE_INTCMD 以降はすべて組み込みのキーワード。
if文の条件式では、キーワードの次が記号で、左括弧 '(' かどうかを調べます。
関数なら「 f ( ... ) 」という形式なので、次に '(' が配置されているはずです。
次が '(' ではない場合は、キーワード単体で書かれているということなので、システム変数として扱います。
※システム変数は、( ) や引数のない関数、とみることができます。
次に、関数だった場合の処理です。コマンドが関数形式で書かれている場合、ProcFunc() に cmd を渡して呼び出し、返値の型とポインタを type_res, ppResult で受け取っています。
ProcFunc() の内部は、こんな感じの構成になります。
※ProcSysvar も同じ構成
cmdfunc() から switch の部分だけ抜き出した感じですね。
varinfo_f() 関数は、HSPの varinfo() を処理するための関数です。( 内容は後述 )
pResult にはそっちで書き込みます。
もし関数形式で使用できないキーワードだったら、HSPERR_UNSUPPORTED_FUNCTION エラーが起きます (「サポートされていない機能 (= 関数としての機能) を呼び出した」)。puterror 関数を呼び出すと、HSPがエラー終了するので、それ以降のコードが実行されることはありません。しかしコンパイラはそれを知らないので、「値が返らないコードパスがある」と警告してきます。開発環境の設定で警告を消すのがいいですが、ここでは簡単にダミーの throw を書いておきました。
最後に、引数を取り出し終わった後、右括弧 ')' がくることを確かめます。
括弧の対応がうまくいっていることはコンパイラが(実行前に)確認済みなのですが。
たとえば引数が 3 つ書かれているのに関数が2つしか受け取らなかった場合などは、次が ')' にならず、エラーが起きます。
そのため「関数のパラメータ記述が無効です」ではなく「引数の数が多すぎます」というエラーを出したいのですが、プラグイン側ではエラーを作成できないんですよね。
それでは、varinfo_f() 関数を作りましょう。先ほど ProcFunc() の中から呼び出されたやつです。
前回の GetVarinfo 命令も使用できるように、varinfo_st() という関数を作りました。
これの中身は前回の GetVarinfo の処理と同じものです。
上記 cmd.h は dllmain.cpp で include しておきます。
varinfo_f() は、varinfo() 関数を実際に処理する関数です。
返値に型タイプ値の int を、引数に pResult へのポインタを指定されます。
実体は以下の通り。
varinfo 関数を処理する関数
難しいところはありませんね。前回とほぼ同じです。
静的変数 result に返値データを格納し、それへのポインタを渡しています。
返値の型は vtResult に設定しますが、現在は int だけなので、初期値のまま変更していません (つまり変数である必要がない)。
上記のソースと第一回のときのソースを参考にコンパイルして、プラグインを作成できます。
恒例の .as ファイルは次のようになります。
// varinfo - public header #ifndef IG_VARINFO_HPI_AS #define IG_VARINFO_HPI_AS #regcmd "_hsp3hpi_init@4", "varinfo.hpi" #cmd varinfo 0x000 // 定数 #enum global VARINFO_LEN0 = 0 #enum global VARINFO_LEN1 #enum global VARINFO_LEN2 #enum global VARINFO_LEN3 #enum global VARINFO_LEN4 #enum global VARINFO_FLAG #enum global VARINFO_MODE #enum global VARINFO_PTR #enum global VARINFO_MAX //######## サンプル・スクリプト ######## #if 1 // 本当はもっと入念にテストしてください (>_<; a = 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 varinfo a(1), info mes "len1 : " + length(a) +"\t: " + info(VARINFO_LEN1) + "\t: " + varinfo(a(1), VARINFO_LEN1) mes "len2 : " + length2(a) +"\t: " + info(VARINFO_LEN2) + "\t: " + varinfo(a(1), VARINFO_LEN2) mes "len3 : " + length3(a) +"\t: " + info(VARINFO_LEN3) + "\t: " + varinfo(a(1), VARINFO_LEN3) mes "len4 : " + length4(a) +"\t: " + info(VARINFO_LEN4) + "\t: " + varinfo(a(1), VARINFO_LEN4) mes "ptr : " + varptr(a(1)) +"\t: " + info(VARINFO_PTR ) + "\t: " + varinfo(a(1), VARINFO_PTR ) mes "flag : " + vartype(a(1)) +"\t: " + info(VARINFO_FLAG) + "\t: " + varinfo(a(1), VARINFO_FLAG) mes "mode : " + info(VARINFO_MODE) +"\t: "+ varinfo(a(1), VARINFO_MODE) stop #endif
BCCの人は _hsp3hpi_init@4 を hsp3hpi_init に変えてください。
varinfoが命令形式でも関数形式でも使えます!!!
これはすごい!!
まさに多重定義!! (違)
以上、reffunc の処理方法でした。
方法は一つではありません。これは僕なりの方法ですし、公式のサンプルでは別の方法を使っています。
もっといい方法を思いついたら、教えてくれるとうれしいです。
では、また次回。
by 上大