Lua 之 userdata

在Lua中可以通过自定义类型(user data)与C语言代码更高效、更灵活的交互,从而扩展Lua能够表达的类型。

full userdata

full userdata 表示一个原始的内存块,可以存储任何东西,它是一个类似于table的object,必须事先创建(也可以被垃圾收集器回收),它也有自己的metatable,它只等于其自身。

可以为每种full userdata 创建一个唯一的元表,来辨别不同类型的userdata,每当创建了一个userdata后,就用相应的元表(放在Registry中)来标记它,而每得到一个userdata后,就检查它是否拥有正确的元表。

Lua在释放full userdata所关联的内存时,若发现userdata对应的元表还有__gc元方法,则会调用这个方法,并以userdata自身作为参数传入。利用该特性,可以再回收userdata的同时,释放与此userdata相关联的资源。

创建一个full userdata:

void *lua_newuserdata (lua_State *L, size_t size);

lua_newuserdata 分配指定大小的内存块,然后将其入栈,并返回内存块地址。

Lua没有为user data预定义任何操作,所以,对user data的操作接口仍由C接口提供,并注册到Lua环境中,供Lua使用。

下面是使用user data实现布尔数组的一个例子:

// foo.c

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <limits.h> #define BITS_PER_WORD (CHAR_BIT * sizeof(int))
#define I_WORD(i) ((unsigned int)(i))/BITS_PER_WORD
#define I_BIT(i) (1 << ((unsigned int)(i)%BITS_PER_WORD)) typedef struct NumArray {
int size;
unsigned int values[];
} NumArray; int newArray(lua_State* L)
{
int i, n; n = luaL_checkint(L,); luaL_argcheck(L, n >= , , "invalid size."); size_t nbytes = sizeof(NumArray) + I_WORD(n - ) * sizeof(int); NumArray* a = (NumArray*) lua_newuserdata(L,nbytes); a->size = n; for (i = ; i < I_WORD(n - ); ++i)
a->values[i] = ; luaL_getmetatable(L, "myarray"); lua_setmetatable(L, -); return ;
} int setArray(lua_State* L)
{
//1. Lua传给该函数的第一个参数必须是userdata,该对象的元表也必须是注册表中和myarray关联的table。
//否则该函数报错并终止程序。
NumArray* a = (NumArray*)luaL_checkudata(L,,"myarray");
int index = luaL_checkint(L,) - ; luaL_checkany(L,); // there are 3 arguments
luaL_argcheck(L,a != NULL,,"'array' expected.");
luaL_argcheck(L, <= index && index < a->size,,"index out of range."); if (lua_toboolean(L,))
a->values[I_WORD(index)] |= I_BIT(index);
else
a->values[I_WORD(index)] &= ~I_BIT(index); return ;
} int getArray(lua_State* L)
{
NumArray* a = (NumArray*)luaL_checkudata(L,,"myarray");
int index = luaL_checkint(L,) - ;
luaL_argcheck(L, a != NULL, , "'array' expected.");
luaL_argcheck(L, <= index && index < a->size,,"index out of range");
lua_pushboolean(L,a->values[I_WORD(index)] & I_BIT(index));
return ;
} int getSize(lua_State* L)
{
NumArray* a = (NumArray*)luaL_checkudata(L,,"myarray");
luaL_argcheck(L,a != NULL,,"'array' expected.");
lua_pushinteger(L,a->size);
return ;
} int array2string(lua_State* L)
{
NumArray* a = (NumArray*)luaL_checkudata(L,,"myarray");
lua_pushfstring(L,"array(%d)",a->size);
return ;
} static luaL_Reg arraylib_f [] = {
{"new", newArray},
{NULL, NULL}
}; static luaL_Reg arraylib_m [] = {
{"set", setArray},
{"get", getArray},
{"size", getSize},
{"__tostring", array2string}, //print(a)时Lua会调用该元方法。
{NULL, NULL}
}; int luaopen_foo(lua_State* L)
{
//1. 创建元表,并将该元表指定给newArray函数新创建的userdata。在Lua中userdata也是以table的身份表现的。
//这样在调用对象函数时,可以通过验证其metatable的名称来确定参数userdata是否合法。
luaL_newmetatable(L,"myarray");
lua_pushvalue(L,-); //2. 为了实现面对对象的调用方式,需要将元表的__index字段指向自身,同时再将arraylib_m数组中的函数注册到
//元表中,之后基于这些注册函数的调用就可以以面向对象的形式调用了。
//lua_setfield在执行后会将栈顶的table弹出。
lua_setfield(L, -, "__index"); //将这些成员函数注册给元表,以保证Lua在寻找方法时可以定位。NULL参数表示将用栈顶的table代替第二个参数。
luaL_register(L, NULL, arraylib_m); //这里只注册的工厂方法。
luaL_register(L,"testuserdata",arraylib_f); return ;
}

编译为C模块,方便Lua调用:

gcc foo.c -shared -fPIC -o foo.so  -llua-5.1 -I /usr/local/include/ 

在Lua中使用上面定义的布尔数组:

require "foo"

local array = testuserdata.new()

print(array:size())     --

for i=, do
array:set(i, i% == )
end for i=, do
print(array:get(i))
end

在Lua中,user data是以table的形式使用。


light userdata

light userdata仅仅表示的是C指针(void*)。

light userdata 就像number类型一样,不需要创建(那自然也不会被垃圾收集器回收),也没有元表,它与所有表示同一指针的light userdata都相等;

创建一个light userdata:

void lua_pushlightuserdata (lua_State *L, void *p);

最新文章

  1. Android Weekly Notes Issue #219
  2. Django基础,Day3 - 编写 django admin
  3. Unity3d游戏场景优化杂谈(3)
  4. CSS布局技巧 -- sticky属性
  5. java基础1.0::Java面向对象、面向对象封装、抽象类、接口、static、final
  6. WCF初探-20:WCF错误协定
  7. GitBook – 使用 GitHub 和 Markdown 制作书籍
  8. py零散知识点
  9. M-JPEG和MPEG-4的区别 M-JPEG VS MPEG
  10. 8种CSS清除浮动的方法优缺点分析
  11. thinkphp学习资料
  12. NSIS:在线下载并安装程序
  13. kali客户端攻击
  14. 学习python的第四天
  15. 了解iOS消息推送一文就够:史上最全iOS Push技术详解
  16. H5 30-CSS元素的显示模式
  17. netstat -ano输出中的ESTABLISHED off
  18. 使用ThinkPHP实现生成缩略图及显示
  19. 性能是.NET Core的一个关键特性
  20. jquery不能是使用普通的for循环 因为普通的for循环通过下表获取对象 如果通过下表获取对象的话 会转成dom对象

热门文章

  1. poj2955括号匹配 区间DP
  2. Win7怎么用IIS发布网站系统 部署项目
  3. 系统间通信(8)——通信管理与RMI 上篇
  4. awk打印出当前行的上一行
  5. 【Codeforces 723C】Polycarp at the Radio 贪心
  6. [学习笔记]tarjan求割点
  7. BP神经网络推导过程详解
  8. Leetcode H-index
  9. Objective-C 中基于RunTime实现的反射
  10. AngularJs $cacheFactory 缓存服务