lua metatable使用和源码分析(二)
浏览:1671次 出处信息
表是如何调度到元表的呢,上篇博文的例子我们看到当键值未能被查找到之后会调用__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 (阅读:5294)
- Lua GC 的源码剖析 (2) (阅读:4606)
- Ameba , 一个简单的 lua 多线程实现 (阅读:4405)
- Lua GC 的源码剖析 (4) (阅读:4210)
- 使用 luajit 的 ffi 绑定 zeromq (阅读:3910)
- Proto Buffers in Lua (阅读:3887)
- Lua GC 的源码剖析 (1) (阅读:3827)
- 一个 Lua 内存泄露检查工具 (阅读:3621)
- Lua GC 的源码剖析 (6) 完结 (阅读:3413)
- Lua GC 的源码剖析 (3) (阅读:3055)
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
扫一扫订阅我的微信号:IT技术博客大学习
<< 前一篇:lua metatable使用和源码分析(一)
后一篇:lua metatable使用和源码分析(三) >>
文章信息
- 作者:hoterran 来源: 运维和开发
- 标签: lua metatable
- 发布时间:2012-09-02 22:28:19
建议继续学习
近3天十大热文
-
[917] WordPress插件开发 -- 在插件使用 -
[135] 解决 nginx 反向代理网页首尾出现神秘字 -
[54] 整理了一份招PHP高级工程师的面试题 -
[53] 如何保证一个程序在单台服务器上只有唯一实例( -
[52] Innodb分表太多或者表分区太多,会导致内 -
[52] 海量小文件存储 -
[51] 全站换域名时利用nginx和javascri -
[51] 用 Jquery 模拟 select -
[50] CloudSMS:免费匿名的云短信 -
[48] jQuery性能优化指南
