定義
固定値(固定式)や決められた文字列をスクリプトに組み込む時、
複数箇所存在する場合に修正が必要となると、実際に使用している場所へ直接記述しているなら面倒です。
かといって、不変であるのに変数を使用するのもどうかという場合は、
変数のようにエイリアスをつけて定義する#define命令を使用してみましょう。
#define エイリアス 定数
エイリアス定数を表す別名を指定する。
定数置換元の固定値(固定式)、固定文字列を指定する。
#define命令はHSP命令の1つですが、今までの命令と違って記号#が使われていますね。 HSPで#が先頭に付く命令は他にも存在し、これからも紹介していくのですが、 プリプロセッサと呼ばれるもので、その名の通り「前処理」となります。 前というのは、プログラムのスクリプトから中間コードへ変換する作業の前のことです。 #define命令は、変数のように名付けられたエイリアスをプログラム開始前に元の定数に変換するものですが、 HSPはオブジェクト指向言語ではなく手続型言語であり、プログラムはスクリプト前方から処理されるので、 #define命令による定義よりもスクリプト前方にエイリアスを書いている場合は変換されません。 そもそも、#define命令のエイリアス名と一緒の変数やラベル、予約語は指定できず、 #define命令より前に使用したエイリアスは変数とみなされ、同じ名前であるとエラーになり実行できません。 説明はコレくらいで終了し、書式について補足しておきます。 他の命令は「ステートメント パラメータ1, パラメータ2, …」という形式でしたが、 #define命令も含むプリプロセッサには「ステートメント 名称 パラメータ1, パラメータ2, …」という ステートメントを除く左端1つ目と2つ目の間もスペースとなっているものが多数存在します。 間違って「#define エイリアス, 定数」と書かないように注意しましょう。 また「#define エイリアス = 定数」でもありませんよ。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#define softname	"SampleName"
#define creater		"ultimate"
#define homepage	"http://www.rinku.zaq.ne.jp/ultimate/"

	title softname
	syscolor 15 : boxf
	pos 10, 10 : button gosub "About", *about
	stop

*about
	dialog "本ソフト名\t" + softname + "\n作者\t" + creater + "\n提供元\t" + homepage
	return
上記の感じに定義しておけば、ソフトを新しく作るときに楽チンです。
さて、「置き換え」がメインの#define命令にはもう1つの顔があるので紹介しておきます。
置換元は定数という書き方をしましたが、#define命令で変数やその他の命令による処理も置き換えられます。
どういうことか?
具体的な例を出すと「#define clear color 255, 200, 200 : boxf : color」とすると、
定義以降は、clearと記述することで淡い赤色でウィンドウ内をクリア表示してくれるようになります。
 1
 2
 3
 4
 5
 6
 7
#define clear color 255, 200, 200 : boxf : color

	clear
	pos 10, 10 : mes "イメージつかない場合は実行してみてください"
	wait 300
	clear
	pos 10, 10 : mes "あたかも「赤色でクリアする1つの命令」のように振舞います"
HSP3.1からは展開ファイル「hsptmp.i」が出力されなくなりましたが、
処理として、先ほどのサンプルは次のように展開して実行されていました。
 1
 2
 3
 4
 5
	color 255, 200, 200 : boxf : color
	pos 10, 10 : mes "イメージつかない場合は実行してみてください"
	wait 300
	color 255, 200, 200 : boxf : color
	pos 10, 10 : mes "あたかも「赤色でクリアする1つの命令」のように振舞います"
このような置き換えをマクロと呼んでいますが、この言葉は以前からも拡張マクロという言い方でありました。
「HSPdir\common\hspdef.as」を開いてみたことはありますか?
この中には幾つものマクロが定義されており、for命令やwhile命令等も入っています。
そう、実はこれらの命令はHSPで用意された命令ではなく、単に定義されたマクロ命令だったんですね。
マクロ命令だったとはいえ、for命令にしろ、while命令にしろ、
プログラム実行時には既に展開されてるはずのものに対して、
可変の値を引数(パラメータ)として与えられるのは変な感じですね。
引数を指定できるようなマクロを特殊展開マクロと呼んでおり、
1つ目の引数を%1、2つ目を%2…として最大32個(%32まで)指定可能となってます。
33個以上指定して実行すると、スクリプトエディタがアベンドしてしまうので気をつけてください。
これらの引数を指定するときは、エイリアス(マクロ名)の後にカッコを付け、関数の要領で記述します。
引数付きマクロの引数を省略してマクロ呼び出しすると実行前にエラーとなってしまいますが、
定義するマクロ側に初期値を設定しておくことで、引数を省略してマクロを呼び出すことが可能となります。
書き方は「#define マクロ(%1 = 初期値) マクロ内容」という感じになります。
 1
 2
 3
#define macroname(%1, %2) mes "引数には" + %1 + "と" + %2 + "が指定されました"

	macroname 123, "test"
引数つきの場合は前述の通りにマクロ定義側をカッコ付きで記述しますが、
マクロ呼出側ではカッコを付けません。カッコ付きで記述したい場合はctypeオプションを使用します。
計算式や文字列に埋め込みむ時にctypeオプションを付けなければうまく動作しなくなるので、
その他の応用範囲は狭いのですがctypeオプションの存在も覚えておきましょう。
 1
 2
 3
 4
 5
 6
 7
#define tax 1.05
#define       addtax1(%1) int(tax * %1)
#define ctype addtax2(%1) int(tax * %1)

	item = 500
	mes "" + item + "円の商品の税込価格は" + addtax1 item  + "円になります" // 正常に表示されない
	mes "" + item + "円の商品の税込価格は" + addtax2(item) + "円になります"
引数以外にも、キーワードを用いて特殊な動作をさせることができます。
「%c」を使うと、1行に書いたマクロを改行して展開してくれますので、
実際には複数行に分けて書く必要があるものもマクロでは1行に書くことができます。
その他のキーワードは異なるマクロでデータを共有させることを目的にしたもので、
マクロを共有させる場合はまず「%t」で共有タグ名をつける必要があります。
共有タグは「%tcommon」という様に、「%t」の後に続けて指定すれば、
同一タグ名を持つ別マクロを共有領域としてこれから紹介するキーワードを使うことができるようになります。
「%n」も「%i」もどちらも、ユニークなラベル文字列を生成するもので、
その違いは、スタックに積む(%i)か積まない(%n)か、だけです。
スタックとはデータを一時的に確保しておく先入れ後出し(後入れ先出し)データ構造の領域のことで、
HSP内部で保持されるものであり、変数のように直接アクセスすることはできません。
スタックに詰まれたデータは「%p」か「%o」で取り出すことが可能で、
「%p」は取り出すもののスタックを降ろしませんので、次回取り出しをしても同一データが取り出されます。
また「%p」には、「%p0」〜「%p9」が用意されており、
「%p0」は数値省略した「%p」と同じ一番目に取り出されるデータを取り出し、
「%p1」は2番目のデータ、「%p2」は3番目のデータ…「%p9」は9番目のデータを取り出します。
当然、スタックを4つしか積んでないと5番目以降は取り出すことができずにエラーとなります。
データを取り出してスタックを降ろすには「%o」を使用しますが、
数値をつけた「%o0」は、スタックを降ろすだけでデータの展開を行いません。
ユニークなラベル文字列ではなくマクロの引数をスタックに積む場合は「%s」を使用します。
スタックに積める引数は1〜9だけで、実際に積む場合は「%s1」〜「%s9」を指定しましょう。
10番目以降の引数を積もうとしてもエラーにはなりませんが正常に積めません。
どうやら、sの後に書いた数字1文字分のみしか見ていないようで、
「%s10」〜「%s19」は「%s1」、「%s20」〜「%s29」は「%s2」として判断されるようです。
もし、仮に15番目の引数を積みたいなら「%i = %15」とすることで同じようにスタックへ積めます。
尚「%s」で積んだスタックの取り出しも「%o」で行います。
以上で特殊キーワードを使ったマクロの説明を終わりますが、使うとすればスタックを扱う上での注意点。
積まれたスタックデータは、必ず降ろさなければならないルール、
言い換えると「%i」と「%o」の個数は「%o」の数と同じでなければならないことを覚えておきましょう。
 1
 2
 3
 4
 5
 6
 7
 8
 9
#define push(%1) %tdata %s1
#define pop %tdata %o

	push 1
	push 22
	push 333
	mes pop
	mes pop
	mes pop
#define命令は置き換えだと書きました。
例えば「#define xxx 1 + 22」と定義して「xxx * 333」を計算させて表示すると、
「1 + 22 * 333」の答えである7327が表示されます。
「#define xxx (1 + 22)」としておくことで「(1 + 22) * 333」の解、7659を得ることができますが、
これから紹介する命令は、計算式の場合に予め計算させた結果を置き換えます。
#const エイリアス 定数
エイリアス定数を表す別名を指定する。
定数置換元の固定値(固定式)を指定する。
不変を意味するCONSTantの名を持つ#const命令は、数値に特化しており文字列は置き換えられません。 小数点以下を含む置き換えは行えますが、 「#const xxx 10.00」のように、小数点以下が0丁度ならば整数値として計算されるようになっており、 「mes xxx * 0.1」は左の実数値だが整数値の型に合わされて0と表示されてしまいます。 順番を変えて「mes 0.1 * xxx」とすれば済む話なのですが、 そういうことじゃなくて定義側を実数値として使いたいんだという場合は、 「#const double xxx 10.00」とdoubleオプションを付ければ、強制的に実数値として扱うようになります。 コレは、定義側を整数値で指定しても実数値になるということなので覚えておきましょう。
 1
 2
 3
 4
 5
#const  value1 9876 - 543
#define value2 9876 - 543

	mes "const  9876 - 543 / 21 = " + (value1 / 21) // 9333 / 21
	mes "define 9876 - 543 / 21 = " + (value2 / 21) // 9876 - (543 / 21)
さて、次は定義されたものを取り消す命令の紹介です。
#undef 取消対象
取消対象取り消す対象を指定する。
#undef命令は設定済みのワードを別の形で再利用できるように解除するUNDEFineの名を持つ命令で、 定義したマクロの他、プリプロセッサを除くHSPの命令や関数なんかもOKです。 ラベルの解除はエラーとなり解除できないことは明白ですが、 変数はエラーにならないので、できてるのかできてないのかがわかりませんが 中身がクリアされたり、配列要素数がそのままなのところから考えると、解除できてないようです。 さて、HSPの命令や関数の定義解除をすると、 その命令・関数は使用できなくなるのかというとそうではありません。 命令・関数の語尾に「@hsp」を付けると、解除した後でも命令・関数を使用することができます。 通常、マクロなんかは自分で決めますから、解除することはほとんどないでしょうけど、 もともとHSPに使われている命令名や関数名を使いたいという場合や、 元々のHSP命令・関数の機能に別の機能も付加して動作させたいという時に解除は役立ちます。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#undef boxf
#define boxf(%1 = 0, %2 = ginfo_winx, %3 = 0, %4 = ginfo_winy) \
	line %1 - 1, %2, %3, %2 : line %1, %2, %1, %4 : line %3, %2, %3, %4 : line %1, %4, %3, %4

	color 255
	boxf 100, 50, 300, 200
	color , 255
	boxf 200, 100, 400, 300
	color , , 255
	boxf 350, 200, 500, 400
	color 150, 150, 150
	boxf@hsp 450, 30, 500, 80 // 元のboxf命令
この章の最後にHSP3で新しく追加された、定数を列挙する命令を紹介して終わります。
#enum エイリアス = 定数
エイリアス定数を表す別名を指定する。
定数割り当てる定数を指定する。省略した場合は直前に指定した定数+1となる。
列挙「ENUMration」を元にした#enum命令は、1つだけの定義ではなく、 複数の連続した値を定義する場合に効果を発揮するものです。 はじめは、変数に値を代入するように「#enum A = B」の形式で定義を行いますが、 連続した値を定義する場合は「#enum C」とだけ定義すると、Cの定数値はB + 1となります。 連続していても定義する値を別の値からにする場合は1番目と同じ「#enum D = E」とすれば、 そこからまた連続した値となります。 尚、#undef命令で連続させて定義した一部を解除しても、解除以降に定義した分はそのままです。 下記サンプルでは、オブジェクトID名の定義として#enum命令を使用しています。 こうしておくと、後で仕様変更することになって間に別のオブジェクトを追加したり、 オブジェクトIDの順番を入れ替えることになっても修正範囲は小さくて済みます。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#enum MAINBOX = 0
#enum LOADBUTTON
#enum SAVEBUTTON
#enum EXITBUTTON

	notesel buf
	mesbox buf, ginfo_winx, ginfo_winy - 20
	objsize ginfo_winx / 3, 20
	pos ginfo_winx / 3 * 0, ginfo_winy - 20 : button gosub "読込", *proc
	pos ginfo_winx / 3 * 1, ginfo_winy - 20 : button gosub "保存", *proc
	pos ginfo_winx / 3 * 2, ginfo_winy - 20 : button gosub "終了", *proc
	stop

*proc
	if stat = EXITBUTTON : end
	if stat = LOADBUTTON {
		dialog "", 16, "読み込むファイル"
		if stat {
			noteload refstr
			objprm MAINBOX, buf
		}
	} else {
		dialog "", 17, "保存名"
		if stat : notesave refstr
	}
	return