IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

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

运维和开发 2012-09-02 22:28:54 累计浏览 1,961 次
本机暂存

基本类型如何使用元表

    在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. 科技爱好者周刊(第 401 期):如何赚到10亿美元 (2026-06-26 08:05:38)
  2. 如何做决策 - 从 Go 的一个 issue 说起 (2026-06-26 08:00:00)
  3. Seven Player:Windows上播放115网盘视频的增强工具 (2026-06-09 00:06:47)

查看更多 开发者 文章 →

建议继续学习

  1. PHP程序的执行流程 (累计阅读 10,032)
  2. 几个内存相关面试题(c/c++) (累计阅读 9,442)
  3. 一个大二学生有关如何成为一名软件工程师的疑问及答复 (累计阅读 9,177)
  4. nginx自定义模块编写-实时统计模块 (累计阅读 8,728)
  5. C语言中史上最愚蠢的Bug (累计阅读 8,086)
  6. websocket 连接 C Server的尝试 (累计阅读 7,922)
  7. Linux C语言编程学习材料 (累计阅读 7,085)
  8. 一次简单C程序的性能优化 (累计阅读 6,850)
  9. 如何学好C语言 (累计阅读 6,432)
  10. C的那些事儿 (累计阅读 6,401)