前回は、msgbox という、パラメータなしの命令を作りました。
今回は引数の受け取り方です。
今回から hspsdk に関する解説になります。
その公式のドキュメントは hspsdk フォルダの hspdll.txt というファイルなので、そちらを参考に読み進めてください。
あるいは、それを読み進める参考にこの講座を利用してください、かも?
パラメータは hspsdk の関数を使って、前から順番に取り出していきます。
code_get... という名前の関数を使います。実際はマクロですが気にしなくてヨロシ。
これは、取り出す引数の型ごとに用意されています。
取得する種類 | 関数名 | 省略値つき関数 | 返値型 |
---|---|---|---|
label | code_getlb | --- | unsigned short* |
str | code_gets | code_getds | char* |
double | code_getd | code_getdd | double |
int | code_geti | code_getdi | int |
変数(var) | code_getva(PVal*) | --- | APTR |
配列(array) | code_getpval | --- | PVal* |
不定 | code_getprm | (PARAM_DEFAULT) | int (定数 PARAM_*) |
省略値つき関数は、省略できる引数を取り出すときに使います。
これらの関数を順番に呼び出して、HSPスクリプト上に書かれるのと同じ順番で、引数の値を取得します。
だいたいこんな感じ:
// 例 cmd arr, v(1), 42, 3.14
// array, var, int, double という順番で引数が与えられるとする PVal* pval1; PVal* pval2; pval1 = code_getpval(); // 配列変数 APTR aptr = code_getva( &pval2 ); // 変数 ( 配列要素を許可 ) int i = code_geti(); double d = code_getdd( 3.14 ); // 省略値つき
ところで、code_getpval() は、PVal* を返します。PVal ってなに?
PVal は構造体です。とりあえず定義を見てみましょう。
// PVAL structure // typedef struct { // Memory Data structure (2.5 compatible) // short flag; // 変数の型タイプ値( vartype ) short mode; // 変数モード (0 = 未使用 / 1 = 通常 / 2 = クローン) int len[5]; // 配列の要素数。[0] は、可変長サイズの時の大きさ int size; // 変数全体のサイズ(bytes) char *pt; // 変数データへのポインタ // Memory Data structure (3.0 compatible) // void *master; // 型が自由に使えるデータ unsigned short support; // 型がサポートするフラグ short arraycnt; // 添字に設定された次元数 int offset; // 配列添字 (APTR) int arraymul; // 添字 → APTR の変換時にかける倍率 } PVal;
※コメントは引用者による。本当は英語で書かれている。( hspvar_core.h )
PVal構造体は、一つの変数のデータを格納する構造体です。
すべての変数はこれで管理されています。
code_getpval() 関数は、引数に指定された変数のPVal構造体へのポインタを返します。
ちなみに、配列の要素数は len の 1, 2, 3, 4 に入っています。([0] は別のモノに使われているので注意)
APTR型は、次のように定義されています。
typedef int APTR;
※ hspvar_core.h より引用。
intですね……。
APTRは、PVal構造体の offset メンバと同じ値で、配列の要素番号を表します。マニュアル( hspdll.txt )いわく、「配列を一次元に並べたときの要素番号」。
とりあえず図で示しましょう。2次元目以降の要素に対応する APTR の値に注意です。
二次元配列とAPTRの対応
とはいえ、ほとんどの場合には APTR 値がそのままもらえるので、あまり気にする必要はないかもしれません。
PVal構造体から正しくデータを取得するには、HspVarProcという構造体が持つ関数ポインタを使います。
HspVarProc は、型ごとに1つずつ存在し、その型の情報や、その値を操作 (取得、加算など) するための関数を持ってます。
とりあえず定義を見てみましょう。
// command execute core function // typedef struct { // データフィールド // short flag; // 型タイプ値 (親アプリケーションで設定されます) short aftertype; // 演算後のタイプ値 short version; // 型タイプランタイムバージョン(0x100 = 1.0) unsigned short support; // サポート状況フラグ(HSPVAR_SUPPORT_*) short basesize; // 1つのデータが使用するサイズ(byte) / 可変長の時は-1 short opt; // (未使用) char *vartype_name; // 型タイプ名文字列へのポインタ char *user; // ユーザーデータ(未使用) // システム参照・型変換用 // void *(*Cnv)( const void *buffer, int flag ); // 型変換( 他→自 ) void *(*CnvCustom)( const void *buffer, int flag ); // 型変換( 自→他 ) PDAT *(*GetPtr)( PVal *pval ); // 実体へのポインタを得る void *(*ArrayObjectRead)( PVal *pval, int *mptype ); // 連想配列からのデータ読み出し void (*ArrayObject)( PVal *pval ); // 連想配列の参照 void (*ObjectWrite)( PVal *pval, void *data, int type); // HSPVAR_SUPPORT_NOCONVERT指定時の代入 void (*ObjectMethod)( PVal *pval ); // 変数に対するメソッドの指定 void (*Alloc)( PVal *pval, const PVal *pval2 ); // 変数メモリを確保する void (*Free)( PVal *pval ); // 変数メモリを解放する int (*GetSize)( const PDAT *pdat ); // 要素が使用するメモリサイズを返す(可変長のため) int (*GetUsing)( const PDAT *pdat ); // 要素が使用中であるかを返す(varuse関数用) // 変数バッファ(バイナリ)のポインタとサイズを返す // (要素が可変長(str)の場合は該当する1配列バイナリのみ) // (要素が固定長(int,double)の場合は全配列バイナリ) // (サイズはメモリ確保サイズを返す) void *(*GetBlockSize)( PVal *pval, PDAT *pdat, int *size ); // バイナリデータ用にメモリブロックを確保する // (要素が可変長(str)の場合にブロックサイズを強制的に確保する) // (固定長の場合は何もしない) void (*AllocBlock)( PVal *pval, PDAT *pdat, int size ); // 代入用関数(型の一致が保障されます) // void (*Set)( PVal *pval, PDAT *pdat, const void *in ); // 演算用関数(型の一致が保障されます) // void (*AddI)( PDAT *pval, const void *val ); void (*SubI)( PDAT *pval, const void *val ); void (*MulI)( PDAT *pval, const void *val ); void (*DivI)( PDAT *pval, const void *val ); void (*ModI)( PDAT *pval, const void *val ); void (*AndI)( PDAT *pval, const void *val ); void (*OrI)( PDAT *pval, const void *val ); void (*XorI)( PDAT *pval, const void *val ); void (*EqI)( PDAT *pval, const void *val ); void (*NeI)( PDAT *pval, const void *val ); void (*GtI)( PDAT *pval, const void *val ); void (*LtI)( PDAT *pval, const void *val ); void (*GtEqI)( PDAT *pval, const void *val ); void (*LtEqI)( PDAT *pval, const void *val ); void (*RrI)( PDAT *pval, const void *val ); void (*LrI)( PDAT *pval, const void *val ); } HspVarProc;
※コメントは一部変更。引用元はhspvar_core.h。
変数を操作するには、それの型に対応する HspVarProc 構造体が持つ関数ポインタを使用します。
この構造体は、グローバル変数 exinfo の HspFunc_getproc() か HspFunc_seekproc() で取得できます。
HspVarProc* vp;
vp = exinfo->HspFunc_getproc( HSPVAR_FLAG_INT );
vp = exinfo->HspFunc_seekproc( "int" );
サンプルのどちらの代入文も、int型のHspVarProc構造体へのポインタを vp (VarProc の略) に代入しています。
HspFunc_getproc() は型タイプ値を、HspFunc_seekproc() は型名を、引数に受け取ります。
exinfo に関しては後述。
標準型の型タイプ値は、定数として定義されています。
これらの型に関しては、この定数を使います。
#define HSPVAR_FLAG_NONE 0 #define HSPVAR_FLAG_LABEL 1 #define HSPVAR_FLAG_STR 2 #define HSPVAR_FLAG_DOUBLE 3 #define HSPVAR_FLAG_INT 4 #define HSPVAR_FLAG_STRUCT 5 #define HSPVAR_FLAG_COMSTRUCT 6
引用元は hspvar_core.h。
★code_getva() と code_getpval() の違い
code_getpval() は配列変数しか受け付けません。例えば、「arr(1)」という引数をこれで取得すると、「配列の要素書式が無効です」というエラーを起こします。#deffunc の array パラメータと同じです。
一方、code_getva(&pval) で「arr(1)」を取得すると、変数 arr の PVal へのポインタが pval に代入され、APTR値 1 が返却されます。#deffunc の var パラメータに対応します。
型によらない値を取り出す code_getprm() 関数は、成功した場合、mpvalというグローバル変数を更新します。mpval も変数を表す PVal* 型です。これに代入されている値が、code_getprm が取り出した値、というわけです。
code_getprm 関数の戻り値は、次の定数のいずれかです。
#define PARAM_OK 0 #define PARAM_SPLIT (-1) #define PARAM_END (-2) #define PARAM_DEFAULT (-3) #define PARAM_ENDSPLIT (-4)
※引用元は hsp3struct.h。負の数値には () をつけましょう。
定数 | 成敗 | 備考 |
---|---|---|
PARAM_OK | 成功 | --- |
PARAM_SPLIT | 成功 | 関数で、最後の引数を取得したときにこれを返します。 |
PARAM_END | 失敗 |
引数を既に取得しきったことを表します。 引数はもうありません。 |
PARAM_DEFAULT | 失敗 |
引数が省略されている、つまり書かれていないことを表します。 省略値がある場合は、その値を使用して処理します。 |
PARAM_ENDSPLIT | 失敗 |
関数の引数を括る ')' を取り出したことを表します。 つまり、関数版の PARAM_END です。引数がもうありません。 |
返値が PARAM_END 以下 (OK, SPLIT 以外) の場合、mpval を参照してはいけません。
ようするに、条件 ( code_getprm() <= PARAM_END ) が成立する場合は失敗です。原因はおそらく引数が省略されたことでしょうから、puterror() に HSPERR_NO_DEFAULT (「引数が省略された」のエラー) を渡し、実行時エラーで終了するべきでしょう。
各種関数の説明だけでやや長めになってしまいました。これを使ったプラグインの例は次に作ります。
では、また次回。
by 上大