この初版はLua 5.0用に書かれました。以降のバージョンと依然として大部分が関連していますが、いくつかの相違点があります。
第4版はLua 5.3に対応しており、Amazonやその他の書店でご購入いただけます。
書籍を購入することにより、Luaプロジェクトの支援にもご協力いただけます。


28.3 – オブジェクト指向アクセス

次のステップは、新しい型をオブジェクトに変換して、`a:size()`などの通常のオブジェクト指向構文を使用してインスタンスを操作できるようにすることです。これは`a.size(a)`と同じです。

    a = array.new(1000)
    print(a:size())     --> 1000
    a:set(10, 3.4)
    print(a:get(10))    --> 3.4

したがって、式`a.size`が`getsize`関数を返すようにする必要があります。ここで重要なメカニズムは`__index`メタメソッドです。テーブルの場合、このメタメソッドはLuaが特定のキーの値を見つけられないときに呼び出されます。ユーザーデータの場合、ユーザーデータにはキーがまったくないため、すべてのアクセス時に呼び出されます。

次のコードを実行するとします。

    local metaarray = getmetatable(array.new(1))
    metaarray.__index = metaarray
    metaarray.set = array.set
    metaarray.get = array.get
    metaarray.size = array.size
1行目で、そのメタテーブルを取得するアレイのみを作成します。これらは`metaarray`に割り当てられます。(Luaからユーザーデータのメタテーブルを設定することはできませんが、制限なしにメタテーブルを取得できます。)次に`metaarray.__index`を`metaarray`に設定します。`a.size`を評価すると、オブジェクト`a`はユーザーデータであるため、Luaはキー`"size"`をオブジェクト`a`で見つけることができません。したがって、Luaは`a`のメタテーブルの`__index`フィールドからこの値を取得しようとします。これは`metaarray`自体になります。しかし、`metaarray.size`は`array.size`であるため、`a.size(a)`は目的どおり`array.size(a)`になります。

もちろん、Cでも同じことを書くことができます。さらに良い方法があります。アレイは、独自の操作があるのでオブジェクトになってからは、テーブル`array`にそれらの操作を含める必要はありません。私たちのライブラリが依然としてエクスポートする必要がある唯一の関数は`new`で、新しいアレイを作成します。他のすべての操作はメソッドとしてのみ提供されます。Cコードは、それらを直接登録できます。

`getsize`、`getarray`、および`setarray`の操作は、以前のアプローチからは変更されていません。変更点は、登録方法です。ライブラリを開く関数を変更する必要があります。まず、正規関数用とメソッド用の2つの別の関数リストが必要です。

    static const struct luaL_reg arraylib_f [] = {
      {"new", newarray},
      {NULL, NULL}
    };
    
    static const struct luaL_reg arraylib_m [] = {
      {"set", setarray},
      {"get", getarray},
      {"size", getsize},
      {NULL, NULL}
    };
ライブラリを開く関数である`luaopen_array`の新バージョンは、メタテーブルを作成し、独自の`__index`フィールドに割り当て、そこにすべてのメソッドを登録し、`array`テーブルを作成して追加する必要があります。`
    int luaopen_array (lua_State *L) {
      luaL_newmetatable(L, "LuaBook.array");
    
      lua_pushstring(L, "__index");
      lua_pushvalue(L, -2);  /* pushes the metatable */
      lua_settable(L, -3);  /* metatable.__index = metatable */
    
      luaL_openlib(L, NULL, arraylib_m, 0);
    
      luaL_openlib(L, "array", arraylib_f, 0);
      return 1;
    }
ここで、`luaL_openlib`の別の機能を使用します。最初の呼び出しでは、`NULL`をライブラリ名として渡すと、`luaL_openlib`は関数をまとめるテーブルを作成せず、代わりにパッケージテーブルがスタックにあると想定します。(適宜アップバリューがあります。)この例では、パッケージテーブルはメタテーブル自体であり、`luaL_openlib`はメソッドを配置します。`luaL_openlib`への次の呼び出しは正常に機能します。指定された名前(`array`)を持つ新しいテーブルを作成し、そこに指定された関数(この場合は`new`のみ)を登録します。

最後の仕上げとして、新しい型に`__tostring`メソッドを追加します。これにより`print(a)`は括弧内に配列のサイズ(たとえば`array(1000)`)をプラスした`array`を出力します。以下が関数そのものです。

    int array2string (lua_State *L) {
      NumArray *a = checkarray(L);
      lua_pushfstring(L, "array(%d)", a->size);
      return 1;
    }
lua_pushfstring 関数は文字列をフォーマットし、スタックのトップに残します。また、配列オブジェクトのメタテーブルに含めるために array2string をリスト arraylib_m に追加する必要があります
    static const struct luaL_reg arraylib_m [] = {
      {"__tostring", array2string},
      {"set", setarray},
      ...
    };