Home -> HSP講座 -> HPI編 No.02

パラメータを受け取ろう

前回は、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の対応
とはいえ、ほとんどの場合には 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。負の数値には () をつけましょう。

code_getprm() の戻り値
定数 成敗 備考
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 上大

第一章へ   第三章へ