lua_newuserdata简单使用 (http://blog.youkuaiyun.com/slionls/article/details/23358795)
userdata (http://www.shouce.ren/api/lua/5/_157.htm)
How free up memory allocated by lua_newuserdata with delete operator? (http://stackoverflow.com/questions/21792657/how-free-up-memory-allocated-by-lua-newuserdata-with-delete-operator)
========================================================================================
原网址:Lua中的userdata (http://www.jellythink.com/archives/587)
轻量级userdata
怎么又有了一个轻量级userdata了?这货又是什么?专业点,叫做“light userdata”。我在前面总结的userdata叫做“full userdata”。
轻量级userdata是一种表示C指针的值(即void *)。由于它是一个值,所以不用创建它。要将一个轻量级userdata放入栈中,只需要调用lua_pushlightuserdata即可。
| voidlua_pushlightuserdata(lua_State*L,void*p); |
尽管两种userdata在名称上差不多,但它们之间还是存在很大不同的。轻量级userdata不是缓冲,只是一个指针而已。它也没有元表,就像数字一样,轻量级userdata不受到垃圾收集器的管理。
轻量级userdata的真正用途是相等性判断。一个完全userdata是一个对象,它只与自身相等。而一个轻量级userdata则表示了一个C指针的值。因此,它与所有表示同一个指针的轻量级userdata相等。可以将轻量级userdata用于查找Lua中的C对象。
现在就来说一种轻量级userdata的使用,还记的我在《再说C模块的编写(2)》中总结的注册表么?谈及注册表的key的时候,说使用UUID是一种不错的方案,现在就使用轻量级的userdata结合static来实现无冲突的key。
|
// 压入轻量级userdata,一个static变量的地址 lua_pushlightuserdata(L,(void*)&key); lua_pushstring(L,"JellyThink"); lua_settable(L,LUA_REGISTRYINDEX); |
由于静态变量的地址在一个进程中具有唯一性,所以绝对不会出现重复key的问题。
|
// 从注册表中取对应的值 lua_pushlightuserdata(L,(void*)&key); lua_gettable(L,LUA_REGISTRYINDEX); |
对于轻量级的userdata,我想我的总结不会到此结束的;但是,在这里也不会做更多的总结的,在以后,遇到了light userdata时,我再根据项目实例进行总结。而今天这篇博文就到此结束了。下一篇再见。
========================================================================================
0、Lua中使用userdata类型来表示在C中定义的类型。userdata只是提供了一块原始的内存区域,可以用来存储任何东西,并且,在lua中userdata没有任何预定义的操作。在C中调用函数lua_newuserdata会根据指定的大小分配一块内存,并将相应的userdata压入栈中,最后返回这个内存块的地址:void *lua_newuserdata(lua_State *L,size_t size)。
lua cclosure 的 upvalue 数量限制
最近写的代码中出了一个奇怪的 bug ,很难调试出来。经过一个晚上的挣扎,终于发现了问题。
第一个问题,在 C 函数中,不能随意的时候 lua_State 中的虚拟机堆栈,如果需要大量使用堆栈,应该先调用 lua_checkstack 。少量使用堆栈,(在 LUA_MINSTACK 20 )之下时则没有问题。这个问题其实在文档里有写,我看过忘记了 :( 不过我个人还是觉得 lua_checkstack 的语义有点奇怪,从字面上看,这个 api 不应该有副作用。它能增加可用堆栈的大小违背了 checkstack 的词义。
第二个问题,当从 lua 调用 C 函数时,当参数数量不足的时候,并不会填入 nil 作为缺省参数。比如,写了一个 C 函数,接受两个参数。当 lua 中调用这个 C 函数时,如果仅传入一个参数,那么在 C 中 stack 上 index 2 位置的值并不一定是 nil 。这个时候我们应该用 lua_gettop 得到准确的参数个数以做适当的处理,或者直接在进入 C 函数时调用一次 lua_settop(L,2) 强制堆栈扩展到两个。
第三个问题,就是一开始最为迷惑我的问题。在生成 cclosure 的时候,upvalue 不能超过 255 个。而这一点并没在文档中说明,运行时压入超过 255 个 upvalue 也不会报错。知道仔细查看源码才发现其中的秘密。
按 lua 的设计,upvalue 的个数应该只受内存大小的限制。但是我的程序在生成一个拥有 258 个 upvalue 的 cclosure 时,upvalue 的个数变成了奇怪的 2 个。我的直觉告诉我,2 == 258%256 。查看源码证实了我的想法,cfunction 的 upvalue 的数量用了一个 byte 记录。究其原因,在于 lua 的 object 用了好几个标记量方便虚拟机运作(比如 gc 用到的 mark 位),当 upvalue 的数量也用一个 byte 的时候,刚好四个 byte 凑齐一个 32bit 字。
为什么我会用到如此之多的 upvalue ,这里就牵扯到最近发现的一个给 lua 写 C 扩展的技巧。
一般,C 对象保存在 lua 中都采用 userdata 的形式。userdata 会参与 gc 的过程,所以不必考虑内存释放的问题。但是,往往 C 对象会对 lua 中其它对象做使用上的引用,而 userdata 本身不具备这个能力。以前我的做法是,使用 lua_ref 得到一个整数引用,放在 userdata 中,到 userdata 的 __gc 被触发或者用户主动释放时,作 lua_unref 操作。这样做不太美观,也有一点点效率问题。
最近发现,简单的 C 扩展,我们完全可以用 C closure 实现,把扩展数据放在 upvalue 中。引用的 lua 对象可以直接放进 upvalue ,而其他 C 数据,可以再生成一个 userdata 写入 upvalue 。这个 closure 可以根据调用时的输入参数决定对它内部的数据做何种操作。
用这个技术,我实现了一个简单的循环队列,它会比用 lua table 实现的队列稍微高效一些。使用时,可以调用 C 扩展函数创建一个队列对象(其实是一个 c closure),然后用这个 closure 做进队列和出队列的操作。当传入参数的时候,就把参数进队列,不传参数的时候就把队首的元素出队列。同时可以根据返回值知道队列时候为空和为满。
========================================================================================
关键函数:
void *lua_newuserdata (lua_State *L, size_t size);
新建full userdata。
(1)分配一块指定大小的内存;
(2)将该full userdata压栈;
(3)返回该内存块的地址给主机程序,主机程序能够随意使用这块内存。
void luaL_argcheck (lua_State *L,int cond,int arg,const char *extramsg);
检查条件是否满足。
void luaL_newmetatable (lua_State *L, const char *tname);
创建userdata可用的metatable。
如果registry已经有tnme键值,则函数返回0;
否则,创建一个[tname, metatable],并放入registry,并返回1。
两种情况下,都会讲tname对应的值入栈。
堆栈+1
void *luaL_checkudata (lua_State *L, int index, const char *tname);
检查在栈中指定位置的对象是否为带有给定名字的metatable(registry中键tname对应的值)的usertata。是则返回userdata地址,否则返回NULL。
void luaL_getmetatable (lua_State *L, const char *tname);
获取registry中的tname对应的metatable,并入栈。注意区分lua_getmetatable函数。
void luaL_setmetatable (lua_State *L, const char *tname);
将栈顶对象的metatable设置为registry表中键tname对应的值。注意区分lua_setmetatable函数。
int lua_getmetatable (lua_State *L, int index);
获取index对应的table的metatable,并入栈。如果该table没有metatable,则返回0,且堆栈不变。
void lua_setmetatable (lua_State *L, int index);
将栈顶的table出栈并设置给index处的值作为metatable。
堆栈-1
(二)利用metatable标识userdata来增加代码的安全性
上面的C库是有缺陷的,比如我们怎么确保例子中setarray的第一个参数就是我们想要的数组userdata,而不是别的不相关的userdata呢?userdata是一种lua类型,它可以用来表示宿主语言中的各种自定义类型对象,为了区分特定类型,我们使用的方法是:
我们单独为该数组创建一个metatable,每次创建数组userdata时,我们设置其和metatable的关联。每次我们访问数组的时候,都检查一下其是否有一个正确的metatable即可。也就是利用不同的metatable来标记不同类型的userdata。因为Lua代码不能够改变userdata的metatable,所以Lua不会伪造我们的代码。
(五)light userdata
light userdata不同于full userdata,它有如下特点:
(1)full userdata代表Lua中的C对象,light userdata代表一个C指针的值(也就是一个void *类型的值)。由于它是一个值,我们不能创建他们(同样的,我们也不能创建一个数字)。
(2)仅仅是一个指针,像数字一样,没有metatables,light userdata不需要垃圾收集器来管理她。
(3)可以用于表示不同类型的对象,我们在Lua中使用light userdata表示C对象。
因为它是一个值,任何指向同一个C地址的light userdata都相等。
void lua_pushlightuserdata (lua_State *L, void *p);
将一个light userdata入栈。
(六)userdata相关的资源释放
Lua以__gc元方法的方式提供了finalizers。这个元方法只对userdata类型的值有效。当一个userdata将被收集的时候,并且userdata有一个__gc域,Lua会调用这个域的值(应该是一个函数):以userdata作为这个函数的参数调用。这个函数负责释放与userdata相关的所有资源,比如说文件描述符、窗口句柄等。
541



被折叠的 条评论
为什么被折叠?



