lua metatable使用和源码分析(二)
浏览:1249次 出处信息
表是如何调度到元表的呢,上篇博文的例子我们看到当键值未能被查找到之后会调用__index事件对应的函数.现在举一个”__add”事件来遍历重要环节的源码.当然从luaV_execute开始.
lvm.c ============= void luaV_execute (lua_State *L, int nexeccalls) { .... switch (GET_OPCODE(i)) { case OP_ADD: { arith_op(luai_numadd, TM_ADD); continue; } ....
arith_op是个宏,会针对操作数进行类型判断,如果是数字类型就会立即进行相加,这就是为什么基本类型即便设置了加减等事件函数后还是没有效果,例如你为数字的加法设置了__add事件后数字相加不会掉用这个元表方法,后面会举一个例子.
继续当发现操作数字为非数字类型后,就会调用Airth函数.
#define arith_op(op,tm) { \\ TValue *rb = RKB(i); \\ TValue *rc = RKC(i); \\ if (ttisnumber(rb) && ttisnumber(rc)) { \\ lua_Number nb = nvalue(rb), nc = nvalue(rc); \\ setnvalue(ra, op(nb, nc)); \\ } \\ else \\ Protect(Arith(L, ra, rb, rc, tm)); \\ }
Airth会对操作数再进行一次努力试图把其转化为数字,这样某些字符串也会被转换成数字相加.如果无法转化成数字则会尝试元表调用call_binTM函数.
static void Arith (lua_State *L, StkId ra, const TValue *rb, const TValue *rc, TMS op) { TValue tempb, tempc; const TValue *b, *c; if ((b = luaV_tonumber(rb, &tempb)) != NULL && (c = luaV_tonumber(rc, &tempc)) != NULL) { lua_Number nb = nvalue(b), nc = nvalue(c); switch (op) { case TM_ADD: setnvalue(ra, luai_numadd(nb, nc)); break; case TM_SUB: setnvalue(ra, luai_numsub(nb, nc)); break; case TM_MUL: setnvalue(ra, luai_nummul(nb, nc)); break; case TM_DIV: setnvalue(ra, luai_numdiv(nb, nc)); break; case TM_MOD: setnvalue(ra, luai_nummod(nb, nc)); break; case TM_POW: setnvalue(ra, luai_numpow(nb, nc)); break; case TM_UNM: setnvalue(ra, luai_numunm(nb)); break; default: lua_assert(0); break; } } else if (!call_binTM(L, rb, rc, ra, op)) luaG_aritherror(L, rb, rc); }
call_binTM函数会对操作数进行尝试获取获取元表再根据事件名字(这里是__add)获取事件函数,如果获取成功则把操作数字压栈并调用这个事件函数.
static int call_binTM (lua_State *L, const TValue *p1, const TValue *p2, StkId res, TMS event) { const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */ if (ttisnil(tm)) tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ if (ttisnil(tm)) return 0; callTMres(L, res, tm, p1, p2); return 1; }
好了以上就是调度元表函数的过程.
我们再来看看最常见的__index,__new_index函数是怎么被调用的,还是luaV_execute.
lvm.c ===================== case OP_GETTABLE: { Protect(luaV_gettable(L, RB(i), RKC(i), ra)); continue; } .... case OP_SETTABLE: { Protect(luaV_settable(L, ra, RKB(i), RKC(i))); continue; }
我们来看查找表,先进行常规的键值查找,如果找到则返回,如果键值未被发现则会查找TM_INDEX对应的元表事件函数.
void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) { int loop; for (loop = 0; loop < MAXTAGLOOP; loop++) { const TValue *tm; if (ttistable(t)) { /* `t\' is a table? */ Table *h = hvalue(t); const TValue *res = luaH_get(h, key); /* do a primitive get */ if (!ttisnil(res) || /* result is no nil? */ (tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */ setobj2s(L, val, res); return; } /* else will try the tag method, cant find so try metamethod */ } else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX))) luaG_typeerror(L, t, "index"); if (ttisfunction(tm)) { callTMres(L, val, tm, t, key); return; } t = tm; /* else repeat with `tm\' */ } luaG_runerror(L, "loop in gettable"); }
luaT_gettmbyobj先查找到元表再查找对应的事件函数
const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event) { Table *mt; switch (ttype(o)) { case LUA_TTABLE: mt = hvalue(o)->metatable; break; case LUA_TUSERDATA: mt = uvalue(o)->metatable; break; default: mt = G(L)->mt[ttype(o)]; } return (mt ? luaH_getstr(mt, G(L)->tmname[event]) : luaO_nilobject); }
回到luaV_gettable找到这个事件函数后就调用callTMres,参数压栈执行函数.
这就是元表事件函数的调用过程,接下来会讲解元表的其它用法,未完待续.
建议继续学习:
- Nginx与Lua (阅读:4672)
- Lua GC 的源码剖析 (2) (阅读:3889)
- Ameba , 一个简单的 lua 多线程实现 (阅读:3568)
- Lua GC 的源码剖析 (4) (阅读:3419)
- 使用 luajit 的 ffi 绑定 zeromq (阅读:3343)
- Proto Buffers in Lua (阅读:3131)
- Lua GC 的源码剖析 (1) (阅读:3057)
- 一个 Lua 内存泄露检查工具 (阅读:3015)
- Lua GC 的源码剖析 (6) 完结 (阅读:2598)
- Lua GC 的源码剖析 (3) (阅读:2325)
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
扫一扫订阅我的微信号:IT技术博客大学习
<< 前一篇:lua metatable使用和源码分析(一)
后一篇:lua metatable使用和源码分析(三) >>
文章信息
- 作者:hoterran 来源: 运维和开发
- 标签: lua metatable
- 发布时间:2012-09-02 22:28:19
建议继续学习
近3天十大热文
- [67] Go Reflect 性能
- [67] Oracle MTS模式下 进程地址与会话信
- [67] 如何拿下简短的域名
- [61] IOS安全–浅谈关于IOS加固的几种方法
- [60] 图书馆的世界纪录
- [59] android 开发入门
- [59] 【社会化设计】自我(self)部分――欢迎区
- [56] 视觉调整-设计师 vs. 逻辑
- [49] 给自己的字体课(一)——英文字体基础
- [47] 界面设计速成