通常来说,C函数需要保留一些非局部的数据,也就是指那些超过他们作用范围的数据。C语言中我们使用全局变量或者static变量来满足这种需要。然而当你为Lua设计一个程序库的时候,全局变量和static变量不是一个好的方法。首先,不能将所有的Lua值保存到一个C变量中。第二,使用这种变量的库不能在多个Lua状态的情况下使用。
一个替代的解决方案是将这些值保存到一个Lua全局变两种,这种方法解决了前面的两个问题。Lua全局变量可以存放任何类型的Lua值,并且每一个独立的状态都有他自己独立的全局变量集。然而,并不是在所有情况下,这种方法都是令人满意地解决方案,因为Lua代码可能会修改这些全局变量,危及C数据的完整性。为了避免这个问题,Lua提供了一个独立的被称为registry的表,C代码可以自由使用,但Lua代码不能访问他。
假索引
LUA_REGISTRYINDEX、LUA_ENVIRONINDEX、 LUA_GLOBALSINDEX是一个假索引(pseudo-indices),一个假索引除了他对应的值不在栈中之外,其他都类似于栈中的索引。 Lua API 中大部分接受索引作为参数的函数,其实可以理解为一张普通表格,你可以使用任何非nil的Lua值来访问她的元素。
The Registry
Lua 提供一个独立的被称为 registry 的表, C 可以自由使用,但 Lua 代码不能访问他。索引:LUA_REGISTRYINDEX,官方解释:
Lua provides a registry, a pre-defined table that can be used by any C code to store whatever Lua value it needs to store. This table is always located at pseudo-index LUA_REGISTRYINDEX. Any C library can store data into this table, but it should take care to choose keys different from those used by other libraries, to avoid collisions. Typically, you should use as key a string containing your library name or a light userdata with the address of a C object in your code
注意的地方:Key值,你可以使用字符串或者C函数的指针以light userdata作为键值。
/* variable with an unique address */
static const char Key = 'k';
/* store a number */
lua_pushlightuserdata(L, (void *)&Key); /* push address */
lua_pushnumber(L, myNumber); /* push value */
/* registry[&Key] = myNumber */
lua_settable(L, LUA_REGISTRYINDEX);
/* retrieve a number */
lua_pushlightuserdata(L, (void *)&Key); /* push address */
lua_gettable(L, LUA_REGISTRYINDEX); /* retrieve value */
myNumber = lua_tonumber(L, -1); /* convert to number */
References
为了解决Key唯一的问题,引入 References:Reference 系统是由辅助库中的一对函数组成,这对函数用来不需要担心名称冲突的将值保存到 registry 中去。
int luaL_ref (lua_State *L, int t);
lua_rawgeti(L, LUA_REGISTRYINDEX, r);
void luaL_unref (lua_State *L, int t, int ref);
luaL_ref Creates and returns a reference, in the table at index t, for the object at the top of the stack (and pops the object). A reference is a unique integer key. As long as you do not manually add integer keys into table t, luaL_ref ensures the uniqueness of the key it returns. You can retrieve an object referred by reference r by calling lua_rawgeti(L, t, r).
If the object at the top of the stack is nil, luaL_ref returns the constant LUA_REFNIL. The constant LUA_NOREF is guaranteed to be different from any reference returned by luaL_ref.
luaL_unref frees a reference and its associated object.
一些相关的优化技巧:
http://blog.codingnow.com/2006/11/lua_c.html “Lua 中写 C 扩展库时用到的一些技巧”
http://blog.codingnow.com/2006/01/_lua.html “Lua中字符串使用优化”
一些问题(待补充)
为什么lua要提供这个区间?有这些变量保存在C或者宿主语言中不是也挺好的吗?
可以理解的是:
- 分散在宿主语言不同部分的Lua交互代码可以很方面的获取全局信息
- 上面提到的,ref速度问题。
但是看了几个实现交互库,都是这样来处理的,其根本的原因是什么呢?