技术头条 - 一个快速在微博传播文章的方式     搜索本站
您现在的位置首页 --> 源码分析 --> lua metatable使用和源码分析(一)

lua metatable使用和源码分析(一)

浏览:1419次  出处信息

    lua metatable(以下简称元表)类似c++的operator overloads,可以对复合结构进行操作,在lua里最常见的就是对表的操作.举例来说,当两个表作加法操作的时候,Lua会检查表的元表中是否有”__add”事件是否对应一个函数(metamethod)。如果存在Lua会调用这个函数来执行一次表的加法操作.

    __index和__newindex是表常常要添加的事件,用于处理键值在表无法被查找到之后的处理.

hoterran@~/Projects/lua$ cat meta_test.lua
t = {}
t.a = 1

print(t.a)
print(t.b)
setmetatable(t, {__index = function(x) return "test" end})
print(t.b)

hoterran@~/Projects/lua$ lua meta_test.lua
1
nil
test

    可见当键值”b”在表内无法查找到之后会调用__index事件对应的匿名函数,后面我们从源码角度分析这个例子.

    接下来源码角度分析各种类型的元表.

     对于基本类型的元表,每类类型只有一个元表,某类类型共享同一个元表,基本类型的元表未设置之前都为空.

lstate.c
================
struct global_State{
  ....
  struct Table *mt[NUM_TAGS];  /* metatables for basic types */
  TString *tmname[TM_N];  /* array with tag-method names */
  ....
}

    mt[NUM_TAGS]用于存储lua各种类型的元表,NUM_TAGS就lua类型个数加一.

mt[1] = {__len = func1, __index = func2 ….}   /*boolean*/
mt[2] = {__len = func1, __index = func2 ….}   /*lightuserdata*/
....
mt[NUM_TAGS]

    tmname[TM_N]用于存储元表事件的字符串,位置与TMS一一对应,这张表在lua启动时会初始化.

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

    查找某个对象类型的元表直接通过对象类型查找元表

(L)->mt[ttype(o)];

    查找元表的事件函数,先从字节码的TMS查找tmname到事件字符串,然后到元表里以事件字符串作为键值查找函数.

luaH_getstr(mt, G(L)->tmname[event])

    对于表和userdata每个对象都有自己独有的元表,是对象私有的元表而不像基本类型是类型共享元表

struct uv {
…
struct table metatable;
...
}
struct table {
…
struct table metatable;
...
}

    lua程序里的setmetatable,getmetatable实际调用的是luaB_setmetatable/luaB_getmetatable两函数,以下是setmetatable的源码.可见在lua程序里只能为表设置元表

lbaselib.c
================
static int luaB_setmetatable (lua_State *L) {
  int t = lua_type(L, 2);
  luaL_checktype(L, 1, LUA_TTABLE);
  luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2,
                    "nil or table expected");
  if (luaL_getmetafield(L, 1, "__metatable"))
    luaL_error(L, "cannot change a protected metatable");
  lua_settop(L, 2);
  lua_setmetatable(L, 1);
  return 1;
}

    而在c程序里,我们就可以对userdata和一些基本类型进行设置元表.使用的函数是lua_setmetatable/lua_getmetatable.

lapi.c
============
LUA_API int lua_setmetatable (lua_State *L, int objindex) {
  TValue *obj;
  Table *mt;
  lua_lock(L);
  api_checknelems(L, 1);
  obj = index2adr(L, objindex);
  api_checkvalidindex(L, obj);
  if (ttisnil(L->top - 1))
    mt = NULL;
  else {
    api_check(L, ttistable(L->top - 1));
    mt = hvalue(L->top - 1);
  }
  switch (ttype(obj)) {
    case LUA_TTABLE: {
      hvalue(obj)->metatable = mt;
      if (mt)
        luaC_objbarriert(L, hvalue(obj), mt);
      break;
    }
    case LUA_TUSERDATA: {
      uvalue(obj)->metatable = mt;
      if (mt)
        luaC_objbarrier(L, rawuvalue(obj), mt);
      break;
    }
    default: {
      G(L)->mt[ttype(obj)] = mt;
      break;
    }
  }
  L->top--;
  lua_unlock(L);
  return 1;
}

    根据类型到不同的位置来设置元表,查找元表简单就不例举了.

    以上是元表的基础知识,接下来讲介绍元表的执行过程.

建议继续学习:

  1. Nginx与Lua    (阅读:4691)
  2. Lua GC 的源码剖析 (2)    (阅读:3906)
  3. Ameba , 一个简单的 lua 多线程实现    (阅读:3594)
  4. Lua GC 的源码剖析 (4)    (阅读:3441)
  5. 使用 luajit 的 ffi 绑定 zeromq    (阅读:3363)
  6. Proto Buffers in Lua    (阅读:3150)
  7. Lua GC 的源码剖析 (1)    (阅读:3078)
  8. 一个 Lua 内存泄露检查工具    (阅读:3041)
  9. Lua GC 的源码剖析 (6) 完结    (阅读:2629)
  10. Lua GC 的源码剖析 (3)    (阅读:2346)
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
© 2009 - 2024 by blogread.cn 微博:@IT技术博客大学习

京ICP备15002552号-1