初版は Lua 5.0 向けに書かれました。以降のバージョンでも基本的には関連していますが、いくつかの違いがあります。
第 4 版は Lua 5.3 を対象としており、Amazon やその他の書店で購入できます。
書籍を購入することで、Lua プロジェクトをサポートすることもできます。


27.3.3 – アップバリュー

レジストリがグローバル値を実装するのに対し、アップバリューメカニズムは特定の関数内でしか参照できない C の静的変数と等価のものを実装します。Lua で新しい C 関数を作成するたびに、必要な数のアップバリューを関連付けることができます。各アップバリューは単一の Lua 値を保持できます。その後、関数が呼び出されると疑似インデックスを使用してそのアップバリューに自由にアクセスできます。

C 関数とそのアップバリューの関連付けをクロージャと呼びます。Lua コードではクロージャは外部関数のローカル変数を使用する関数であることを思い出してください。C クロージャは Lua クロージャに対する C 近似です。クロージャに関する興味深い事実の 1 つは、同じ関数コードを使用しても、アップバリューが異なれば異なるクロージャを作成できることです。

簡単な例として、C で newCounter 関数を作成します。(この同じ関数は、セクション 6.1で Lua ですでに定義しています。)この関数はファクトリ関数です。呼び出されるたびに新しいカウンタ関数を返します。すべてのカウンタは同じ C コードを共有しますが、それぞれ独自の独立したカウンタを保持します。ファクトリ関数は次のようになります。

    /* forward declaration */
    static int counter (lua_State *L);
    
    int newCounter (lua_State *L) {
      lua_pushnumber(L, 0);
      lua_pushcclosure(L, &counter, 1);
      return 1;
    }
ここでは、新しいクロージャを作成する lua_pushcclosure 関数が重要です。2 番目の引数は基本関数(例では counter)、3 番目はアップバリューの数(例では 1)です。新しいクロージャを作成する前に、そのアップバリューの初期値をスタックにプッシュする必要があります。例では、単一のアップバリューの初期値として数値 0 をプッシュします。lua_pushcclosure は、想定どおり新しいクロージャをスタックに残すため、クロージャは newCounter の結果として返される準備ができています。

これで、counter の定義を見ていきましょう。

    static int counter (lua_State *L) {
      double val = lua_tonumber(L, lua_upvalueindex(1));
      lua_pushnumber(L, ++val);  /* new value */
      lua_pushvalue(L, -1);  /* duplicate it */
      lua_replace(L, lua_upvalueindex(1));  /* update upvalue */
      return 1;  /* return new value */
    }
ここでは、キーとなる関数はlua_upvalueindex(実際にはマクロ)で、アップバリュの疑似インデックスを作成します。この疑似インデックスもスタックインデックスと同じですが、スタックには存在しません。式lua_upvalueindex(1)は、関数の最初のアップバリュのインデックスを表します。したがって、関数counterの中のlua_tonumberは、最初の(そして唯一の)アップバリュの現在の値を数値として取得します。その後、関数counterは新しい値++valをプッシュし、そのコピーを作成し、そのうちのコピーを使用してアップバリュを新しい値に置き換えます。最後に、別の値を戻り値として返します。

Luaのクロージャとは異なり、Cのクロージャはアップバリュを共有できません。各クロージャには独自の独立したセットがあります。ただし、さまざまな関数のアップバリュを共通のテーブルを参照するように設定できます。そうすると、このテーブルはそれらの関数がデータを共有できる共通の場所になります。