lua metatable使用和源码分析(一)
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; }
根据类型到不同的位置来设置元表,查找元表简单就不例举了.
以上是元表的基础知识,接下来讲介绍元表的执行过程.
建议继续学习:
- Nginx与Lua (阅读:4671)
- Lua GC 的源码剖析 (2) (阅读:3889)
- Ameba , 一个简单的 lua 多线程实现 (阅读:3567)
- Lua GC 的源码剖析 (4) (阅读:3419)
- 使用 luajit 的 ffi 绑定 zeromq (阅读:3342)
- Proto Buffers in Lua (阅读:3131)
- Lua GC 的源码剖析 (1) (阅读:3057)
- 一个 Lua 内存泄露检查工具 (阅读:3015)
- Lua GC 的源码剖析 (6) 完结 (阅读:2597)
- Lua GC 的源码剖析 (3) (阅读:2325)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:hoterran 来源: 运维和开发
- 标签: lua metatable
- 发布时间:2012-09-02 22:27:47
- [68] Go Reflect 性能
- [68] 如何拿下简短的域名
- [67] Oracle MTS模式下 进程地址与会话信
- [62] IOS安全–浅谈关于IOS加固的几种方法
- [61] 图书馆的世界纪录
- [60] 【社会化设计】自我(self)部分――欢迎区
- [58] android 开发入门
- [56] 视觉调整-设计师 vs. 逻辑
- [49] 给自己的字体课(一)——英文字体基础
- [48] 读书笔记-壹百度:百度十年千倍的29条法则