Lua解释器源码(四):metatable 元表
目录

通常,Lua中的每个类型都有一套预先定义的操作,比如数字类型预先定义了加法, 但是table类型就不提供相加操作。

如果需要,我们可以给table类型定义相加操作,这就需要元表,通过元表给一个类型定义其操作。 这个定义的操作也叫做元方法(metamethod/tagmethod)。

在Lua代码中,通常用Table实现各种复杂数据结构,并且只有Table可以设置元表。 其它类型的元表必须通过C代码来完成。

在Table的结构中,其有个成员struct Table *metatable;,它的作用就是指向Table的元表。 另一个成员lu_byte flags;,它的作用是用位图标记哪些元方法可以使用。 因此需要定义一个Table类型并定义某些元方法,这样才能作为元表。

可以定义的元方法类型如下:

static const char *const luaT_eventname[] = {  /* ORDER TM */
"__index", "__newindex",
"__gc", "__mode", "__len", "__eq",
"__add", "__sub", "__mul", "__div", "__mod",
"__pow", "__unm", "__lt", "__le",
"__concat", "__call"
};

typedef enum {
TM_INDEX,
TM_NEWINDEX,
TM_GC,
TM_MODE,
TM_LEN,
TM_EQ,  /* last tag method with `fast' access */
TM_ADD,
TM_SUB,
TM_MUL,
TM_DIV,
TM_MOD,
TM_POW,
TM_UNM,
TM_LT,
TM_LE,
TM_CONCAT,
TM_CALL,
TM_N        /* number of elements in the enum */
} TMS;

其中包含运算符:

  • __add 加
  • __sub 减
  • __mul 乘
  • __div 除
  • __mod 模
  • __pow 幂
  • __unm 取相反数
  • __concat 连接

比较操作符:

  • __eq 等于
  • __lt 小于
  • __le 小于等于

和其它一些方法。

在Lua State初始化的时候,Lua把元方法的字符串名放入了全局表里, 这样不必在每次做元方法查询时都压入元方法的名字(和lua_getfield不同?)。

void luaT_init (lua_State *L) {
static const char *const luaT_eventname[] = {  /* ORDER TM */
"__index", "__newindex",
"__gc", "__mode", "__len", "__eq",
"__add", "__sub", "__mul", "__div", "__mod",
"__pow", "__unm", "__lt", "__le",
"__concat", "__call"
};
int i;
for (i=0; itmname[i] = luaS_new(L, luaT_eventname[i]);
luaS_fix(G(L)->tmname[i]);  /* never collect these names */
}
}

const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event) {
Table *mt;
switch (ttypenv(o)) {
case LUA_TTABLE:
mt = hvalue(o)->metatable;
break;
case LUA_TUSERDATA:
mt = uvalue(o)->metatable;
break;
default:
mt = G(L)->mt[ttypenv(o)];
}
return (mt ? luaH_getstr(mt, G(L)->tmname[event]) : luaO_nilobject);
}

最后,通过这两个宏并配合flags快速获得tagmethods。

#define gfasttm(g,et,e) ((et) == NULL ? NULL : \
((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e]))

#define fasttm(l,et,e)  gfasttm(G(l), et, e)

参考

  • 风云:Lua源码欣赏

发表评论