第 1 版は Lua 5.0 向けに執筆されました。後続のバージョンでもほとんど関連性は変わりませんが、いくつかの違いがあります。
第 4 版は Lua 5.3 を対象にしており、Amazon やその他の書店で購入できます。
この本を購入することで、Lua プロジェクトのサポートにもなります。


24.2 – スタック

Lua と C の間で値を交換しようとして直面する問題は 2 つあります。動的型システムと静的型システムの不一致、および自動メモリ管理と手動メモリ管理の不一致です。

Lua では、a[k] = v と記述する場合、kv は複数の異なる型を持つことができます(a もメタテーブルにより異なる型を持つ可能性があります)。ただし、C でこの操作を提供する場合、すべての settable 関数は固定された型を持つ必要があります。この単一の操作には、それぞれ 3 つの引数の型を組み合わせたものに対して多数の異なる関数が必要になります(関数 1 つにつき 1 つ)。

Lua のすべての値を表すことができる、lua_Value と呼びましょう、ある種の union 型を C で宣言することで、この問題を解決できます。その場合、settable は以下のように宣言できます。

    void lua_settable (lua_Value a, lua_Value k, lua_Value v);
このソリューションには 2 つの欠点があります。まず、このような複雑な型を他の言語にマップするのは困難です。Lua は C/C++ だけではなく、Java、Fortran などとも簡単にインターフェイスできるよう設計されています。次に、Lua はガベージコレクションを行います。Lua の値を C 変数内に保持した場合、Lua エンジンはこの使用法が認識できません。この値はガベージであると(誤って)判断して、この値を収集する可能性があります。

そのため、Lua API は lua_Value 型のようなものはいっさい定義していません。代わりに、抽象的なスタックを使用して、Lua と C の間で値を交換します。このスタック内の各スロットは任意の Lua 値を保持できます。Lua から値(グローバル変数の値など)を要求する場合、Lua を呼び出すと、必要な値がスタックにプッシュされます。値を Lua に渡す場合は、最初に値をスタックにプッシュし、その後で Lua を呼び出します(値がポップされます)。C 型をスタックにプッシュするための関数と、スタックから値を取得するための関数はそれぞれ異なりますが、組み合わせ爆発は回避されます。さらに、このスタックは Lua により管理されるので、ガベージコレクターは C が使用している値を認識します。

API 内のほぼすべての関数はスタックを使用します。最初の例で示したように、luaL_loadbuffer は結果をスタック上に(コンパイル済みチャンクかエラーメッセージ)残します。lua_pcall は呼び出される関数をスタックから取得し、エラーメッセージがあればそこに残します。

Lua は、厳格な LIFO 方式(Last In, First Out、つまり常に上部から)でこのスタックを操作します。Lua を呼び出すと、スタックの上位部分のみが変更されます。C コードはより自由度が高く、特に、スタック内の任意の要素を調べることができ、任意の位置に要素を挿入および削除することさえできます。