2.4 メタテーブルとメタメソッド

Lua の全ての値はメタテーブル (metatable) を持つことができます。このメタテーブルは通常の Lua テーブルであり、関連付いた値の特定のイベントに対する振る舞いを定義します。値が持つメタテーブルの特定のフィールドを設定することで、様々な場面における値の振る舞いを変更できます。例えば数値でない値が加算演算のオペランドになった場合には、Lua はその値のメタテーブルの __add フィールドに関数があるかを調べ、もし見つかればその関数を呼んで「加算」を実行します。

あるイベントに対応するメタテーブルのフィールドのキーは、イベントの名前の先頭にアンダースコアを二つ付けた文字列です。対応するバリューはメタバリュー (metavalue) と呼ばれます。たいていのイベントではメタバリューが関数であり、この場合はその関数をメタメソッド (metamethod) と呼びます。ただし特別な設定をしなければ任意の呼び出せる値をメタメソッドに設定でき、関数だけではなく __call メタメソッドを持つ値もメタメソッドにできます。

任意の値のメタテーブルは getmetatable 関数を使って確認できます。Lua がメタテーブル内のメタメソッドを問い合わせるときは直接アクセスが使われます (参照: rawget)。

テーブル型の値のメタテーブルは setmetatable 関数で設定できます。テーブル以外の型の値については、メタテーブルを Lua コードから変更することはできません。ただし debug ライブラリを使えば行えます (参照: § 6.10)。

テーブルとフルユーザーデータはそれぞれの値が個別のメタテーブルを持ちますが、複数のテーブルやフルユーザーデータが同じメタテーブルを共有することは可能です。他の全ての型では型ごとに一つのメタテーブルがあり、それが全ての値で共有されます。例えば全ての数値で使われるメタテーブルが一つあり、全ての文字列で使われるメタテーブルが一つあるといった形です。デフォルトではどんな値もメタテーブルを持ちませんが、文字列ライブラリは文字列型の値に対するメタテーブルを設定します (参照: § 6.4)。

メタテーブルが管理する処理の詳しいリストを次に示します。各イベントは対応するキーで識別されます。慣習により、Lua が使うメタテーブルのキーは二つのアンダースコアと小文字アルファベットからなります。

ここに示したキーに加えて、Lua インタープリタは __gc (§ 2.5.3)・__close (§ 3.3.8)・__mode (§ 2.5.4)・__name にも対応します。__name エントリーに設定された文字列は tostring やエラーメッセージで利用されることがあります。

単項演算子 (否定・長さの取得・ビットごとの NOT) では、メタメソッドはダミーの第二引数 (第一引数と同じ値) と共に呼ばれます。この追加のオペランドは Lua の内部コードを単純にする (単項演算子を二項演算子と同じように扱う) ためだけにあるので、大部分のユーザーはこれを気にしなくて構いません。

メタテーブルは通常のテーブルと同じなので、上で定義したイベント名でないフィールドも保持できます。標準ライブラリに含まれる関数の一部 (例えば tostring) はメタテーブルの他のフィールドを利用します。

テーブルに必要なメタメソッドを全て追加してからオブジェクトのメタテーブルを設定するのが良い習慣です。特に __gc メタメソッドはこの順番で設定した場合にのみ意味を持ちます (参照: § 2.5.3)。メタテーブルをオブジェクトを作成した直後に設定するのも良い習慣です。

広告