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 (阅读:5294)
- Lua GC 的源码剖析 (2) (阅读:4605)
- Ameba , 一个简单的 lua 多线程实现 (阅读:4404)
- Lua GC 的源码剖析 (4) (阅读:4210)
- 使用 luajit 的 ffi 绑定 zeromq (阅读:3910)
- Proto Buffers in Lua (阅读:3887)
- Lua GC 的源码剖析 (1) (阅读:3827)
- 一个 Lua 内存泄露检查工具 (阅读:3620)
- Lua GC 的源码剖析 (6) 完结 (阅读:3413)
- Lua GC 的源码剖析 (3) (阅读:3055)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:hoterran 来源: 运维和开发
- 标签: lua metatable
- 发布时间:2012-09-02 22:27:47
-
[917] WordPress插件开发 -- 在插件使用 -
[135] 解决 nginx 反向代理网页首尾出现神秘字 -
[54] 整理了一份招PHP高级工程师的面试题 -
[52] Innodb分表太多或者表分区太多,会导致内 -
[52] 海量小文件存储 -
[52] 如何保证一个程序在单台服务器上只有唯一实例( -
[51] 全站换域名时利用nginx和javascri -
[50] 用 Jquery 模拟 select -
[49] CloudSMS:免费匿名的云短信 -
[48] jQuery性能优化指南
