lua userdata 汇总

lua_newuserdata简单使用  (http://blog.csdn.net/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)。

     1、实质在C中定义lua的userdata,与定义C模块完全类似,只不过通常这时需要通过调用lua_newuserdata来告诉lua分配一块额外的内存,而在内存所有相关的操作都是在C中的定义的,实质就是C模块中的接口。注意这块分配的额外内存是由Lua垃圾收集器来管理的,无须关心起释放等情况。
     2、在实现一个Lua的程序库或userdate,必须保证该库或userdata的接口不应破坏C数据或在Lua中导致core dump。
     3、可以为每种userdata创建一个唯一的元表,来辨别不同类型的userdata,每当创建了一个userdata后,就用相应的元表来标记它,而每得到一个userdata后,就检查它是否拥有正确的元表,注意Lua代码中不能改变userdata的元表(当能增加已有元表的属性,比如对元表key为__index赋值)。通常是将这个元表存储在注册表中,也类型名作为key,元表为value。辅助库提供了一些函数来实现这些:
      int luaL_newmetatable(lua_State*L, const char *tname);
      void luaL_getmetatable(lua_State *L,const char *tnaem);
      void *luaL_checkudata(lua_State*L,int index,const char *tname);
     4、轻量级userdata是一种表示C指针的值(即void*),要将一个轻量级userdata放入栈中,只需要调用lua_pushlightuserdata即可。轻量级userdata只是一个指针而已。它没有元表,就像数字一样,轻量级userdata无须受垃圾收集器的管理。
     5、Lua在释放完全userdata所关联的内存时,若发现userdata对应的元表还有__gc元方法,则会调用这个方法,并以userdata自身作为参数传入。利用该特性,可以再回收userdata的同时,释放与此userdata相关联的资源。
 
========================================================================================

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相关的所有资源,比如说文件描述符、窗口句柄等。

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值