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

Luaのすべての値は、メタテーブルを持つことができる。このメタテーブルは通常のLuaテーブルであり、特定のイベントにおける元の値の動作を定義する。メタテーブルの特定のフィールドを設定することで、値の動作のいくつかの側面を変更できる。たとえば、数値でない値が加算のオペランドになった場合、Luaはその値のメタテーブルにある__addフィールドの関数を調べる。関数が見つかると、Luaは加算を行うためにその関数を呼び出す。

メタテーブルにおける各イベントのキーは、イベント名の先頭に2つのアンダースコアを付けた文字列であり、対応する値はメタ値と呼ばれる。ほとんどのイベントでは、メタ値は関数でなければならず、その関数はメタメソッドと呼ばれる。前の例では、キーは文字列「__add」であり、メタメソッドは加算を行う関数である。特に断りがない限り、メタメソッドには、関数または__callメタメソッドを持つ値のいずれかである、任意の呼び出し可能な値を実際には使用できる。

任意の値のメタテーブルは、getmetatable関数を使って問い合わせられる。Luaは生のアクセス(rawgetを参照)を使って、メタテーブル内のメタメソッドを問い合わせる。

テーブルのメタテーブルは、setmetatable関数を使って置き換えられる。デバッグライブラリ(§6.11)を使用する場合を除き、Luaコードから他の型のメタテーブルを変更することはできない。

テーブルとフルユーザーデータは個別のメタテーブルを持つが、複数のテーブルやユーザーデータがメタテーブルを共有することもできる。その他のすべての型の値は、型ごとに1つのメタテーブルを共有する。すなわち、すべての数値に1つ、すべての文字列に1つ、といった具合である。デフォルトでは値にメタテーブルはないが、文字列ライブラリはstring型にメタテーブルを設定する(§6.5を参照)。

メタテーブルが制御する操作の詳細な一覧を次に示す。各イベントは、対応するキーによって識別される。慣例として、Luaが使用するすべてのメタテーブルキーは、2つのアンダースコアに小文字のラテン文字を続けて構成される。

  • __add : 加算(+)操作。加算のいずれかのオペランドが数値でない場合、Luaはメタメソッドの呼び出しを試みる。最初に第1オペランド(それが数値であっても)を調べ、そのオペランドが__addのメタメソッドを定義していなければ、第2オペランドを調べる。メタメソッドが見つかった場合、Luaは2つのオペランドを引数としてメタメソッドを呼び出し、その呼び出し結果(1つの値に調整される)が操作の結果となる。見つからない場合、Luaはエラーを発生させる。
  • __sub : 減算(-)操作。加算操作と同様に動作する。
  • __mul : 乗算(*)操作。加算操作と同様に動作する。
  • __div : 除算(/)操作。加算操作と同様に動作する。
  • __mod : 剰余(%)操作。加算操作と同様に動作する。
  • __pow : べき乗(^)操作。加算操作と同様に動作する。
  • __unm : 符号反転(単項-)操作。加算操作と同様に動作する。
  • __idiv : 切り捨て除算(//)操作。加算操作と同様に動作する。
  • __band : ビット単位AND(&)操作。いずれかのオペランドが整数でも整数へ型強制できる浮動小数点数でもない場合にLuaがメタメソッドを試みる点を除き、加算操作と同様に動作する(§3.4.3を参照)。
  • __bor : ビット単位OR(|)操作。ビット単位AND操作と同様に動作する。
  • __bxor : ビット単位排他的OR(2項~)操作。ビット単位AND操作と同様に動作する。
  • __bnot : ビット単位NOT(単項~)操作。ビット単位AND操作と同様に動作する。
  • __shl : ビット単位左シフト(<<)操作。ビット単位AND操作と同様に動作する。
  • __shr : ビット単位右シフト(>>)操作。ビット単位AND操作と同様に動作する。
  • __concat : 連結(..)操作。いずれかのオペランドが文字列でも数値(常に文字列へ型強制できる)でもない場合にLuaがメタメソッドを試みる点を除き、加算操作と同様に動作する。
  • __len : 長さ(#)操作。オブジェクトが文字列でない場合、Luaはそのメタメソッドを試みる。メタメソッドがある場合、Luaはオブジェクトを引数として呼び出し、その呼び出し結果(常に1つの値に調整される)が操作の結果となる。メタメソッドはないがオブジェクトがテーブルの場合、Luaはテーブルの長さ操作を使用する(§3.4.7を参照)。それ以外の場合、Luaはエラーを発生させる。
  • __eq : 等価(==)操作。比較する値が両方ともテーブルであるか、両方ともフルユーザーデータであり、かつプリミティブには等しくない場合にだけLuaがメタメソッドを試みる点を除き、加算操作と同様に動作する。呼び出し結果は常にブール値へ変換される。
  • __lt : 未満(<)操作。比較する値が両方とも数値でも両方とも文字列でもない場合にだけLuaがメタメソッドを試みる点を除き、加算操作と同様に動作する。さらに、呼び出し結果は常にブール値へ変換される。
  • __le : 以下(<=)操作。未満操作と同様に動作する。
  • __index : インデックスアクセス操作table[key]。このイベントは、 tableがテーブルでないか、 keytableに存在しない場合に発生する。メタ値はtableのメタテーブルから検索される。このイベントのメタ値には、関数、テーブル、または__indexメタ値を持つ任意の値を指定できる。関数の場合、tablekeyを引数として呼び出され、その呼び出し結果(1つの値に調整される)が操作の結果となる。それ以外の場合、最終結果はkeyを使ってこのメタ値をインデックス指定した結果となる。このインデックス指定は生ではなく通常のものであるため、別の__indexメタ値を起動することがある。
  • __newindex : インデックス代入table[key] = value。indexイベントと同様に、このイベントはtableがテーブルでないか、 keytableに存在しない場合に発生する。メタ値はtableのメタテーブルから検索される。インデックス指定と同様に、このイベントのメタ値には、関数、テーブル、または__newindexメタ値を持つ任意の値を指定できる。関数の場合、tablekeyvalueを引数として呼び出される。それ以外の場合、Luaは同じキーと値を使い、このメタ値に対してインデックス代入を繰り返す。この代入は生ではなく通常のものであるため、別の__newindexメタ値を起動することがある。 __newindexメタ値が呼び出されるとき、Luaはプリミティブな代入を行わない。必要であれば、メタメソッド自身がrawsetを呼び出して代入を行える。
  • __call : 呼び出し操作func(args)。このイベントは、Luaが関数でない値を呼び出そうとした場合(すなわちfuncが関数でない場合)に発生する。メタメソッドはfuncから検索される。存在する場合、元の呼び出しの引数(args)に先立ち、 funcを第1引数としてメタメソッドが呼び出される。その呼び出しのすべての結果が操作の結果となる。複数の結果を許可するメタメソッドはこれだけである。 前の一覧に加えて、インタープリターはメタテーブル内の次のキーも使用する。__gc§2.5.3を参照)、__close§3.3.8を参照)、__mode§2.5.4を参照)、__name。(エントリー__nameに文字列が格納されている場合、tostringやエラーメッセージで使用されることがある。)

単項演算子(符号反転、長さ、ビット単位NOT)の場合、メタメソッドは、第1オペランドと等しいダミーの第2オペランドを付けて計算・呼び出される。この追加オペランドは、これらの演算子を2項演算と同様に動作させてLua内部を単純化するためだけのものであり、将来のバージョンで削除される可能性がある。ほとんどの用途では、この追加オペランドは関係しない。

メタテーブルは通常のテーブルであるため、前述のイベント名だけでなく、任意のフィールドを格納できる。標準ライブラリの一部の関数(例:tostring)は、それぞれの目的でメタテーブル内の他のフィールドを使用する。

必要なすべてのメタメソッドをテーブルへ追加してから、そのテーブルを何らかのオブジェクトのメタテーブルとして設定するのがよい慣行である。特に、__gcメタメソッドは、この順序に従った場合にだけ機能する(§2.5.3を参照)。また、オブジェクトを作成した直後にそのメタテーブルを設定することも、よい慣行である。