在Lua C API编程上,经常有一些博客会说,必须使用luaL_newmetatableluaL_setmetatable来给userdata加元表。还说给userdata加的元表不是普通的table。于是本着不信邪的态度,我翻了一下源码并自己尝试写了一些测试。

普通的表能做userdata的元表么

对于这个问题,我觉得是可以的,因为Lua中只有这一种数据结构,不存在什么特别的表。

class A
{public:A() { cout << "A ctor " << this << endl; }~A() { cout << "A dtor " << this << endl; }
};int close_A(lua_State* L)
{A* pa=static_cast<A*>(lua_touserdata(L, 1));cout << "Closing A: " << pa << endl;pa->~A();return 0;
}int test(lua_State* L)
{auto ptr = new (lua_newuserdata(L, sizeof(A))) A;lua_newtable(L);lua_pushcfunction(L, close_A);lua_setfield(L, -2, "__gc");lua_setmetatable(L, -2);return 1;
}int main()
{auto L = luaL_newstate();luaL_openlibs(L);lua_register(L, "test", test);if (luaL_loadstring(L, "t=test()")) {cout << "Failed to load string." << endl;cout << lua_tostring(L, -1) << endl;}else if (lua_pcall(L, 0, 1, NULL)) {cout << "Error: " << lua_tostring(L, -1) << endl;}lua_close(L);return 0;
}

运行结果

A ctor 001EF910
Closing A: 001EF910
A dtor 001EF910

可以看到,__gc元方法被正确的调用了,因此元表无论在Lua层还是在C层都跟普通的表没有区别。

luaL_*metatable做了什么

通过翻看Lua源码可以弄清楚这一点。在lauxlib.hlauxlib.c中:

#define luaL_getmetatable(L,n)   (lua_getfield(L, LUA_REGISTRYINDEX, (n)))LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) {if (luaL_getmetatable(L, tname) != LUA_TNIL)  /* name already in use? */return 0;  /* leave previous value on top, but return 0 */lua_pop(L, 1);lua_createtable(L, 0, 2);  /* create metatable */lua_pushstring(L, tname);lua_setfield(L, -2, "__name");  /* metatable.__name = tname */lua_pushvalue(L, -1);lua_setfield(L, LUA_REGISTRYINDEX, tname);  /* registry.name = metatable */return 1;
}LUALIB_API void luaL_setmetatable (lua_State *L, const char *tname) {luaL_getmetatable(L, tname);lua_setmetatable(L, -2);
}LUALIB_API void *luaL_testudata (lua_State *L, int ud, const char *tname) {void *p = lua_touserdata(L, ud);if (p != NULL) {  /* value is a userdata? */if (lua_getmetatable(L, ud)) {  /* does it have a metatable? */luaL_getmetatable(L, tname);  /* get correct metatable */if (!lua_rawequal(L, -1, -2))  /* not the same? */p = NULL;  /* value is a userdata with wrong metatable */lua_pop(L, 2);  /* remove both metatables */return p;}}return NULL;  /* value is not a userdata with a metatable */
}LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) {void *p = luaL_testudata(L, ud, tname);if (p == NULL) typeerror(L, ud, tname);return p;
}

从中不难看出,luaL_newmetatable先尝试获取与tname绑定的一个表,如果不为nil,则返回0,但此时该表已经获取并放在了栈上。如果为nil,则在栈上新建一个{__name=$tname}这样的表,然后复制一份,并通过lua_setfield设置为某个表中对应键为$tname的值,返回1。

luaL_setmetatable显然是先获取绑定的表,然后将表设置为原栈顶元素的元表。需要注意的是luaL_setmetatable不会自动新建元表,因此当获取一个不存在的表时,会引起逻辑上的错误。

luaL_getmetatable是一个很简单的宏,但这里的LUA_REGISTRYINDEX多少让人有些迷惑。其实Lua官方手册已经给出了解释. 大意就是,这是一个伪索引,对应着一个全局表 (不妨成为registry) ,其真正存储位置不在Lua的虚拟栈上,但是可以通过操作栈的函数来操作这个表。这个表只能通过C API来操作访问,Lua层无法访问到这个表。可以用来存储模块数据等。

所以其实luaL_*metatable只是将registry表作为参照,将新建的表存入到registry中。luaL_checkudata也不过是将userdata的元表和registry中存储的元表进行直接比较(rawequal)。

还有一点需要注意,在luaL_newmetatable中,新建的元表有__name键。此键类似于其他语言的"类型",是Lua解释器用来显示信息所用的元属性。在Lua层中就可以尝试. 例如:

mt={__name="JustName"}
t={}
setmetatable(t,mt)
print(type(t),t)

运行结果是:

table   JustName: 0000000000779e10

Lua Userdata 的元表 (Metatable)相关推荐

  1. Lua 元表(Metatable)

    在 Lua table 中我们可以访问对应的key来得到value值,但是却无法对两个 table 进行操作. 因此 Lua 提供了元表(Metatable),允许我们改变table的行为,每个行为关 ...

  2. lua的元表metatable及元方法

    前言 元表对应的英文是metatable,元方法是metamethod.我们都知道,在C++中,两个类是无法直接相加的,但是,如果你重载了"+"符号,就可以进行类的加法运算.在Lu ...

  3. lua 给userdata设置元表_提高Lua语言开发效率的简单方法

    概述 首先,lua是一门高效的(efficient).轻量级(lightweight)的嵌入式脚本语言(embeddable scripting language),这是它的官方网站的标语. 其次,l ...

  4. lua面向对象封装及元表(metatable)性能测试

    Lua本身是没有面向对象支持的,但面向对象编程在逻辑复杂的大型工程却很有用.于是很多人用Lua本身的数据结构table来模拟面向对象.最简单的一种方法是把对象的方法.成员都放到table中.如: -- ...

  5. lua中给userdata绑定元表示例

    前言 这篇博客,我估计写不好.一方面是内容挺绕,一方面是我没有看过书,是照葫芦画瓢写代码. 前置要求: Lua调用C代码 lua中表与元表 Lua操作C语言用户自定义类型数据Userdata 上面第三 ...

  6. lua userdata 汇总

    lua_newuserdata简单使用  (http://blog.csdn.net/slionls/article/details/23358795) userdata (http://www.sh ...

  7. lua userdata

    2019独角兽企业重金招聘Python工程师标准>>> userdata类型是为了方便C/C++对Lua进行扩展,因为在用C/C++扩展时,我们经常会自定义数据类型,如: typed ...

  8. LuaForUnity7:Lua表与元表

    一.万能数据类型:table 之前讲过的数组其实就是table的一种特殊形态,而table更像是一个会变形的精灵,它可以是字典.可以是链表.也可以是数组,就看你想要如何使用 换句话说:table是Lu ...

  9. lua——userdata使用

    userdata说明: 0.Lua中使用userdata类型来表示在C中定义的类型.userdata只是提供了一块原始的内存区域,可以用来存储任何东西,并且,在lua中userdata没有任何预定义的 ...

最新文章

  1. (五)开源IT资产管理系统--分发OCS客户端
  2. boost::pfr::detail::offset_based_getter相关的测试程序
  3. gnuradio上怎么使用python文件_使用Python从PDF文件中提取数据
  4. linux-02-常用的命令-必须掌握
  5. 云计算学习教程,Python自动化运维开发实战
  6. Spark推荐实战系列之Swing算法介绍、实现与在阿里飞猪的实战应用(附代码)
  7. 在 jquery repeater 中添加设置日期,下拉,复选框等控件
  8. 计算机网络断开后怎么连接,电脑网络断开怎样重新连接
  9. 2021年十佳返利优惠券平台排名如下
  10. 基于CC2430的基础实验4-----定时器中断
  11. poj-2115 C Looooops 扩展欧几里德算法求最小非负整数解
  12. 人工智能基础入门清单(计算机视觉、强化学习方向/领域)
  13. 2019年最实用的导航栏设计实践和案例分析全解
  14. 打开虚拟机时报硬盘UUID 已经存在的解决办法
  15. Django学习-app创建与注册
  16. Bugzilla 的安装
  17. 企业做CMMI3级认证,不满足条件可以做吗?
  18. zabbix探究告警触发器Triggers
  19. 计算机不能上网怎么检查,电脑不能上网如何通过Ping命令检查网络
  20. LINUX下常见搜索文件方法

热门文章

  1. 为什么我的word一打开就显示启动失败,然后要用安全模式打开?解决方法有2
  2. 打游戏千万不要扣计算机,这些新手劝退的游戏,别玩!有毒,玩了你想砸电脑!...
  3. 使用spleeter对音乐分轨(人声伴奏 分离)
  4. traceback.print_exc()的用法
  5. Edsger Dijkstra经典言论 (ZT)
  6. 非技术文章,今天很凄凉
  7. 我用DW也有几年了,真不知道你说的这些代码是什么?不是快捷键吧?
  8. 如何折一个牛逼的纸飞机
  9. 【多模块聚合工程】IDEA搭建SpringBoot多模块聚合工程详细步骤
  10. 品牌为Synaptics的触控板如何外接鼠标后自动禁用触控板