通常來說,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速度問題。
但是看了幾個實現交互庫,都是這樣來處理的,其根本的原因是什么呢?