4.1 スタック

Lua と C の間で行われる値のやり取りには仮想スタック (virtual stack) が利用されます。スタックの各要素は nil・数値・文字列といった Lua の値を表し、API の関数はパラメータとして受け取る Lua ステートを通してスタックにアクセスします。

Lua が C の関数を呼ぶと、呼び出された関数は新しいスタックを受け取ります。このスタックはそれまでのスタックや他の C 関数で使われている関数とは無関係です。スタックには最初 C 関数へ渡される引数が入っていて、一次的な Lua の値や呼び出し側へ返す値を入れるのに利用されます (lua_CFunction)。

簡単のため、API に含まれる多くの関数はクエリ操作で厳密なスタック操作 (プッシュとポップ) を使いません。その代わりスタックにはインデックスを使ってアクセスできるようになっています。正のインデックスはスタック内の絶対位置を表し、1 がスタックの底を指します。負のインデックスはスタックトップからの相対オフセットを表します。さらに細かく言うと、スタックに n 要素が積まれているとき、インデックス 1 は最初の要素 (最初にスタックにプッシュされた要素) を表し、n は最後の要素を表します。インデックス -1 が表すのは最後の要素 (スタックトップにある要素) で、-n が表すのは最初の要素です。

4.1.1. スタックのサイズ

Lua API と値をやり取りするとき、一貫性を保証するのはプログラマーの責任です。特にスタックオーバーフローを起こさないようにする責任はプログラマーにありますlua_checkstack 関数を使えばスタックに新しい要素をプッシュするだけの空間を確保できます。

Lua が C 関数を呼び出すときは、スタックの空き容量が最低でも LUA_MINSTACK だけあることが保証されます。LUA_MINSTACK は 20 と定義されているので、要素をスタックに積むループなどがない限り C 側でスタックの空き容量を気にする必要はありません。

C から Lua 関数を返り値の数を指定せずに (lua_call で) 呼ぶとき、Lua は返り値の分の空間をスタックに確保します。しかしそれ以上はなにも保証しないので、関数を呼んだ後はスタックにプッシュする前に lua_checkstack を行うべきです。

4.1.2. 有効なインデックスと処理可能なインデックス

スタックのインデックスを受け取る API 関数が受け取るのは有効なインデックス (valid index) あるいは処理可能なインデックス (acceptable index) です。

有効なインデックスとは変更可能な Lua の値が保存された位置を指すインデックスであり、スタックの通常のインデックスと疑似インデックス (pseudo-index) からなります。スタックのインデックスは 1 からスタックトップまでの値 (1 ≤ abs(index) ≤ top) を取り、疑似インデックスはスタックに含まれない値 (レジストリ (§ 4.3) や C 関数のアップバリュー (§ 4.2)) へアクセスするための何らかの位置を表します。

変更可能な変数の位置を必要とせず値だけを必要とする関数 (クエリ関数など) は処理可能なインデックスを受け付けます。有効なインデックスは処理可能なインデックスですが、それに加えてスタックが確保した空間でスタックトップより上の部分 (スタックサイズまでの部分) も処理可能となります (0 はどんなときも処理可能なインデックスにはなりません)。アップバリュー (§ 4.2) へのインデックスで現在の C 関数が持つアップバリューの数より大きい値は有効でない処理可能なインデックスです。そうでないと書かれていない限り、API に含まれる関数は処理可能なインデックスを受け取ります。

処理可能なインデックスはスタックトップを確認することなくスタックの要素を調べるためにあります。例えば C 関数が第三引数の値を問い合わせるときは、第三引数の存在 (つまり、3 が有効なインデックスかどうか) を確認せずとも処理を行えます。

処理可能なインデックスと共に呼び出される関数では、有効でないインデックスに対応する値は仮想的な型 LUA_TNONE を持っているものとされ、nil と同じように振る舞います。

広告