c函数的状态
一般在c函数中保存non-local data时可以使用全局或者静态变量.但如果c library供lua调用的话,这种方法是不可行的(一则涉及lua和c的类型转换,二者此类函数不能被多个lua state使用).此时可以将其保存为lua global variable,但这样lua代码也是可以操作这些数据的.因此lua提供了一个独立的table,叫做registry,c能自由使用但lua不能.
The registry
1 | lua_pushstring(L, "Key"); |
伪索引,LUA_REGISTRYINDEX指向了registry,如上代码取出field为Key的value值
References
reference system由auxlib中的一批函数组成,其作用在于使用者不需要担心生成的key是否唯一(防止在registry中同样的key被覆盖),例如如下函数:
1 | int r = luaL_ref(L, LUA_REGISTRYINDEX); |
会将栈顶的值放入registry,key为r,r是一个整形数字.所以如果不使用reference system,不要使用整形数作为registry的key.
r被称为一个reference.
反向操作会取出registry中key为r的值,并且放到栈中
1 | lua_rawgeti(L, LUA_REGISTRYINDEX, r); |
如下操作释放key和value
1 | luaL_unref(L, LUA_REGISTRYINDEX, r); |
Upvalues
registry解决global value的问题,upvalues解决static value的问题,所以只对特定的函数可见.c函数和upvalues组合为一个closure.
例如:
1 | /* forward declaration */ |
lua_pushcclosure将一个c函数指针以及一个upvalues压入,首先将一个upvalue的初始值(本例中为0)压入.
counter的定义如下:
1 | static int counter (lua_State *L) { |
lua_upvalueindex会将upvalue取出,取出之后+1然后压栈,复制一份该值然后用该值更新upvalue,最后将+1的值返回.
User-Defined Types In C
前边我们讲了如何使用c函数扩展lua,本节讲解如何使用c结构扩展lua
例如如下一个double类型的数组结构:
1 | typedef struct NumArray { |
Userdata
如下函数申请size大小的内存,然后将指针放到栈顶,lua中的userdata类型
1 | void *lua_newuserdata (lua_State *L, size_t size); |
申请空间:
1 | static int newarray (lua_State *L) { |
设置数据:
1 | static int setarray (lua_State *L) { |
获取数据:
1 | static int getarray (lua_State *L) { |
获取数组大小:
1 | static int getsize (lua_State *L) { |
注册进lua:
1 | static const struct luaL_reg arraylib [] = { |
使用:
1 | a = array.new(1000) |
metatables
1 | int luaL_newmetatable (lua_State *L, const char *tname); |
上述三个函数分别生成一个metatable(名称为tname),获取该metable,并且检测index位置处的userdata是否匹配tname指定的metatable,不匹配返回null,否则返回userdata的地址
lua_setmetatable将指定index的元素的metatable设置为栈顶的metatable
Object-Oriented Access
为了使用面向对象的语法,例如:
1 | a = array.new(1000) |
我们需要将__index设置一下:
1 | local metaarray = getmetatable(array.new(1)) |
那么在c中可以按如下方法:
1 | static const struct luaL_reg arraylib_f [] = { |
Array Access
为了使用数组语法,例如a:get(i)为a[i],可以如下设置
1 | local metaarray = getmetatable(newarray(1)) |
c中为:
1 | int luaopen_array (lua_State *L) { |
Light Userdata
light userdata是一个函数指针
1 | void lua_pushlightuserdata (lua_State *L, void *p); |
Managing Resources
full userdata的资源回收由lua管理,只是一段内存.light userdata不由lua管理,还有一些其他资源例如file descriptors,由lua的__gc metatable管理,类似于finalizer或者destructor
修改目录遍历函数,如下:
1 | int luaopen_dir (lua_State *L) { |
设置LuaBook.dir的__gc字段为dir_gc,dir_gc负责回收Dir这个资源.设置一个全局变量dir为l_dir函数.
1 | static int dir_gc (lua_State *L) { |
Dir资源为一个userdata
1 |
|
设置userdata Dir的metable为LuaBook.dir.然后打开目录,设置一个闭包,函数为dir_iter,upvalue为栈顶的userdata
1 | static int dir_iter (lua_State *L) { |
遍历目录
lua中使用方法如下:
1 | for fname in dir(".") do print(fname) end |