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

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

浏览:1436次  出处信息

基本类型如何使用元表

    在lua里只能为表设置元表,而在c程序里面可以为基本类型进行元表操作,但上篇博文提到了普通类型的很多操作是不会走到元表,下面的例子针对数字类型,添加多种事件,只有部分事件会生效.下面的例子在c代码里对数字类型添加元表对__add,__len都设置事件.

mct.c
======================
#include
#include
#include 

//__add event function
static int add(lua_State *L) {
    lua_pushinteger(L, 200);
    return 1;
}
//__len event function
static int len(lua_State *L) {
    lua_pushinteger(L, 111);
    return 1;
}

int main(int argc, char* argv[]) {
    lua_State *L = lua_open();
    luaopen_base(L);

    //integer type metatable
    lua_pushnumber(L, 1);
    lua_newtable(L);

    lua_pushcfunction(L, add);
    lua_setfield(L, -2, "__add");
    lua_pushcfunction(L, len);
    lua_setfield(L, -2, "__len");

    lua_setmetatable(L, -2);

    if(luaL_loadfile(L, "meta_c_test.lua") || lua_pcall(L, 0, 0, 0)) {
        printf("error %s\\n", lua_tostring(L, -1));
        return;
    }

    lua_close(L);

    return 0;
}

    再来看meta_c_test.lua脚本

hoterran@~/Projects/lua$ cat meta_c_test.lua
print(#1)
print(1 + 2)

print(getmetatable(1))
print(getmetatable(""))

hoterran@~/Projects/lua$ ./mct
100
3
table: 0x87e7950
nil

    这个lua程序在普通的lua解释器下是回报错的,因为数字类型并未有计算长度的函数.当给数字类型设置元表后,__len成功的执行了,而__add并未调用元表事件函数,这符合我们在上一篇博文里的分析.同时可以发现数字类型已经设置了一个元表,而其它基本类型的元表都是为nil的.

利用 _metatable事件来保护元表不会被修改

    _metatable可以隐藏元表信息,一旦设置后元表就不能再被修改了.

hoterran@~/Projects/lua$ cat meta_metatable.lua
d = {}
setmetatable(d, {__index = function(x) return 111 end})
print(d.aa)                 ---- 111
print(getmetatable(d))
setmetatable(d, {__metatable = "aaaa"})
print(getmetatable(d))       ---- metable信息被隐藏,打印出__metatable里信息aaaa
--cant call
--setmetatable(d, {__index = function(x) return 222 end})  ---这句会报错,元表不再能被修改.

    设置元表的时候如果发现有__metatable的事件信息则报错退出,停止设置元表

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;
}

    查找元表之后,如果还存在__metatable事件,返回该事件对应的信息并把已经获取到的元表弹出栈.

static int luaB_getmetatable (lua_State *L) {
    luaL_checkany(L, 1);
    if (!lua_getmetatable(L, 1)) {
        lua_pushnil(L);
        return 1;  /* no metatable */
    }
    luaL_getmetafield(L, 1, "__metatable");
    return 1;  /* returns either __metatable field (if present) or metatable */
}

利用元表来保护userdata不被误用

    因为userdata是个指针容易被误用,所以元表的作用就是给它取个名字,使得在操作userdata的时候比较名字是否一致.这里代码不展示了,介绍一些原理.

     luaL_newmetatable会创建一个键值名和空元表挂到LUA_REGISTRYINDEX,

     lua_setmetatable为userdata设置元表之后,操作userdata的时候会调用lua_checkudata根据键值从REGISTRYINDEX获得这个元表并与userdata内的元表进行比较看是否相同,否则不能操作这个userdata.

    好了基本上metatable的用法就是这些,有不对的地方还请帮忙指出,谢.

建议继续学习:

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

京ICP备15002552号-1