在C 函數中保存狀態:registry、reference和upvalues
在C函數中保存狀態:registry、reference和upvalues
C函數能夠通過堆棧來和Lua交換數據,但有時候C函數須要在函數體的作用域之外保存某些Lua數據。那麽我們想到全局變量或static變量,這樣做的缺點是:(1)為Lua設計C函數庫時,導致不可重入。(2)不是全部的Lua值都能非常好的保存到C變量中。那麽可不能夠將值保存在Lua全局變量裏面呢,能夠,Lua就提供了一個獨立的被稱為registry的表,可是Lua代碼本身不能訪問它。
1、registry全局註冊表
解釋:一個普通的Lua表,使用假索引(pseudo-index)LUA_REGISTRYINDEX訪問。C代碼能夠訪問。Lua代碼不能訪問。
用途:解決C函數保留全局Lua值的問題。
註意:全部的C庫共享同樣的registry,所以對於key的命名須要具有全局唯一性。
// 獲取registry表鍵值"KEY"相應的值的方法: lua_pushstring(L, "KEY"); lua_gettable(L, LUA_REGISTRYINDEX);
2、reference引用系統
解釋:通過一個整數來唯一標識一個Lua數據對象,由兩個函數luaL_ref和luaL_unref組成,這對函數用來不須要操心名稱沖突的將值保存到registry中去。
用途:將一個指向Lua值的reference存儲到一個C結構體中,這個reference是一個int的KEY。
註意:棧頂值為nil的時候。不會產生reference。luaL_ref函數會返回LUA_REFNIL,而對LUA_REFNIL解引用是沒有效果的。
重要函數:
int luaL_ref (lua_State *L, int t);
創建並返回一個引用reference。並將[reference。棧頂值v]增加t相應的表中。
void luaL_unref (lua_State *L, int t, int ref);
解引用,將t相應的表中的[reference。v]鍵值對刪除。
// 對棧頂的值v生成一個引用,即將[r, v]存到LUA_REGISTRYINDEX表中 int r = luaL_ref(L, LUA_REGISTRYINDEX); // 將一個引用值入棧 lua_rawgeti(L, LUA_REGISTRYINDEX, r); // 解引用。即釋放reference和值 luaL_unref(L, LUA_REGISTRYINDEX, r);
3、upvalues機制
解釋:當創建一個C函數時能夠關聯一些值,這樣就創建了一個C閉包,這些關聯值就叫做upvalues。
用途:實現了與C static變量等價的概念,這樣的變量僅僅能在特定的函數內可見。
使用:通過lua_upvalueindex(n)生成假索引來訪問。
// 預聲明 static int counter (lua_State *L); // 創建C閉包的工廠函數 int newCounter (lua_State *L) { lua_pushnumber(L, 0); lua_pushcclosure(L, &counter, 1); return 1; } // C函數 static int counter (lua_State *L) { double val = lua_tonumber(L, lua_upvalueindex(1)); lua_pushnumber(L, ++val); /* new value */ lua_pushvalue(L, -1); /* duplicate it */ lua_replace(L, lua_upvalueindex(1)); /* update upvalue */ return 1; /* return new value */ }
註意:永遠不要使用數字作為registry 的key。由於這樣的類型的key是保留給reference系統使用。
假索引(pseudo-index)的特點:(1)相應的值不在棧中。(2)使用方式類似於棧索引。大多數接受索引為參數的函數都能使用。(3)那些操作棧本身的函數不能使用假索引,比方lua_remove,lua_insert等。
與Lua閉包(在Lua代碼中,一個閉包是一個從外部函數訪問局部變量的函數)不同的是。C閉包不能共享upvalues:每個閉包都有自己獨立的變量集。然而。我們能夠設置不同函數的upvalues指向同一個表。這樣這個表就變成了一個全部函數共享數據的地方。
在C 函數中保存狀態:registry、reference和upvalues