初版はLua 5.0向けに書かれました。最新のバージョンでも依然として多くの情報が関連していますが、いくつかの違いがあります。
第4版はLua 5.3を対象としており、Amazonなどの書店で購入できます。
書籍を購入することで、Luaプロジェクトをサポートすることもできます。


28.1 - ユーザーデータ

最初の懸念事項は、Luaで配列値を表現する方法です。Luaはこのための基本タイプであるユーザーデータを用意しています。ユーザーデータは、Luaでは事前定義された操作のない生のメモリ領域を提供します。

Lua APIは以下の関数を提供して、ユーザーデータを作成します。

    void *lua_newuserdata (lua_State *L, size_t size);
lua_newuserdata関数は指定されたサイズのメモリブロックを割り当て、対応するユーザーデータをスタックに押し込み、ブロックアドレスを返します。何らかの理由で他の手段でメモリを割り当てる必要がある場合、ポインタサイズのユーザーデータを作成して、実際のメモリブロックへのポインタを格納するのは非常に簡単です。この手法の例は次の章で説明します。

lua_newuserdataを使用すると、新しい配列を作成する関数は次のようになります。

    static int newarray (lua_State *L) {
      int n = luaL_checkint(L, 1);
      size_t nbytes = sizeof(NumArray) + (n - 1)*sizeof(double);
      NumArray *a = (NumArray *)lua_newuserdata(L, nbytes);
      a->size = n;
      return 1;  /* new userdatum is already on the stack */
    }
(luaL_checkint関数は整数用のluaL_checknumberの亜種です。) newarrayがLuaに登録されると、a = array.new(1000)のような文で新しい配列を作成できます。

エントリを格納するには、array.set(array, index, value)のような呼び出しを使用します。後ほど、メタテーブルを使用して、より一般的な構文array[index] = valueをサポートする方法について説明します。どちらの表記でも、基になる関数は同じです。Luaで通常と同様に、インデックスは1から始まると想定します。

    static int setarray (lua_State *L) {
      NumArray *a = (NumArray *)lua_touserdata(L, 1);
      int index = luaL_checkint(L, 2);
      double value = luaL_checknumber(L, 3);
    
      luaL_argcheck(L, a != NULL, 1, "`array' expected");
    
      luaL_argcheck(L, 1 <= index && index <= a->size, 2,
                       "index out of range");
    
      a->values[index-1] = value;
      return 0;
    }
luaL_argcheck関数は特定の条件をチェックし、必要に応じてエラーを発生させます。したがって、setarrayを誤った引数で呼び出すと、次のような明瞭なエラーメッセージが表示されます。
    array.set(a, 11, 0)
    --> stdin:1: bad argument #1 to `set' (`array' expected)

次の関数はエントリを取得します。

    static int getarray (lua_State *L) {
      NumArray *a = (NumArray *)lua_touserdata(L, 1);
      int index = luaL_checkint(L, 2);
    
      luaL_argcheck(L, a != NULL, 1, "`array' expected");
    
      luaL_argcheck(L, 1 <= index && index <= a->size, 2,
                       "index out of range");
    
      lua_pushnumber(L, a->values[index-1]);
      return 1;
    }
配列のサイズを取得するための別の関数を定義します。
    static int getsize (lua_State *L) {
      NumArray *a = (NumArray *)lua_touserdata(L, 1);
      luaL_argcheck(L, a != NULL, 1, "`array' expected");
      lua_pushnumber(L, a->size);
      return 1;
    }
最後に、ライブラリを初期化するための追加のコードが必要です。
    static const struct luaL_reg arraylib [] = {
      {"new", newarray},
      {"set", setarray},
      {"get", getarray},
      {"size", getsize},
      {NULL, NULL}
    };
    
    int luaopen_array (lua_State *L) {
      luaL_openlib(L, "array", arraylib, 0);
      return 1;
    }
ここでも、補助ライブラリからluaL_openlibを使用します。これは特定の名前 (この例では"array") のテーブルを作成し、配列arraylibで指定される名前-関数のペアでそれを埋めます。

ライブラリをオープンした後、Luaで新しいタイプを使用する準備が整いました。

    a = array.new(1000)
    print(a)               --> userdata: 0x8064d48
    print(array.size(a))   --> 1000
    for i=1,1000 do
      array.set(a, i, 1/i)
    end
    print(array.get(a, 10))  --> 0.1

この実装をPentium/Linuxで実行すると、予想どおり100K要素の配列には800KBのメモリが使用されます。一方、同等のLuaテーブルには1.5MB以上です。