什麼叫做在C函數中保存狀態?比如你現在使用Lua調用了C函數Func1,但是Func1中有一些數據在調用完以後保存下來,供以後使用。而這些數據就是所謂的狀態,也就是我們需要保存的東東。有人就會說了,Lua調用C時,把所有的需要保存的狀態都返回到Lua中,當調用下一個函數時,將需要的狀態當做參數再傳進去,不錯,是一個辦法,但是很麻煩。方法一:注冊表;方法二:環境;方法三:upvalue。
注冊表是一個全局的table,它只能被C代碼訪問。通常,可以用它來保存那種需要在幾個模塊中共享的數據;
但是,如果需要保存一個模塊的私有數據,那麼應該使用環境,與Lua函數一樣,每個C函數都有自己的環境table,通常情況下,一個模塊內的所有函數共享同一個環境table,由此它們可以共享數據。
最後,C函數也可以擁有upvalue,upvalue是一種與特定函數相關聯的Lua值。
#includeusing namespace std; #include void registryTestFunc(lua_State* L) { lua_pushstring(L,"Hello"); lua_setfield(L,LUA_REGISTRYINDEX,"key1"); lua_getfield(L,LUA_REGISTRYINDEX,"key1"); printf("%s\n",lua_tostring(L,-1));//輸出為:Hello } int main() { lua_State* L = luaL_newstate(); registryTestFunc(L); lua_close(L); return 0; }
#includetest.lua文件內容using namespace std; #include int setValue(lua_State* L) { luaL_checkstring(L,-1); lua_pushvalue(L,-1); lua_setfield(L,LUA_ENVIRONINDEX,"key1"); return 0; } int getValue(lua_State* L) { lua_getfield(L,LUA_ENVIRONINDEX,"key1"); return 1; } static luaL_Reg myfuncs[] = { {"setValue", setValue}, {"getValue", getValue}, {NULL, NULL} }; extern "C" __declspec(dllexport) int luaopen_testenv(lua_State* L) { lua_newtable(L); //創建一個新的表用於環境 lua_replace(L,LUA_ENVIRONINDEX); //將剛剛創建並壓入棧的新表替換為當前模塊的環境表。 luaL_register(L,"testenv",myfuncs); return 1; //這個注冊函數比以前寫的注冊函數要多兩行代碼,先要創建一個新的table,然後調用lua_replace將新的table作環境table。然後調用luaL_register時,所有新建的函數都會繼承當前環境。 }
require "testenv" local fun1 = function() local var = "Hello,world!!!" testenv.setValue(var) print(testenv.getValue()) --輸出Hello,world!!! end xpcall(fun,print) os.execute("pause")上面先將值設置到模塊環境table中。然後再從中取出來。這個和上面說的注冊表有很多的相似之處。盡管可能使用環境來代替注冊表,但是如果沒有在不同模塊之間共享數據的需要,就盡可能的不要使用注冊表;使用環境創建的引用,只是在本模塊中可見,這樣縮小了數據的使用范圍了,減小了數據被錯改的可能,增加了數據的安全性。
#includetest.lua文件內容using namespace std; #include int counter(lua_State* L) { //獲取第一個upvalue的值。 int val = lua_tointeger(L,lua_upvalueindex(1)); //將得到的結果壓入棧中。 lua_pushinteger(L,++val); //賦值一份棧頂的數據,以便於後面的替換操作。 lua_pushvalue(L,-1); //該函數將棧頂的數據替換到upvalue(1)中的值。同時將棧頂數據彈出。 lua_replace(L,lua_upvalueindex(1)); //lua_pushinteger(L,++value)中壓入的數據仍然保留在棧中並返回給Lua。 return 1; } int newCounter(lua_State* L) { //壓入一個upvalue的初始值0,該函數必須先於lua_pushcclosure之前調用。 lua_pushinteger(L,0); //壓入閉包函數,參數1表示該閉包函數的upvalue數量。該函數返回值,閉包函數始終位於棧頂。 lua_pushcclosure(L,counter,1); return 1; } static luaL_Reg myfuncs[] = { {"counter", counter}, {"newCounter", newCounter}, {NULL, NULL} }; extern "C" __declspec(dllexport) int luaopen_testupvalue(lua_State* L) { luaL_register(L,"testupvalue",myfuncs); return 1; }
require "testupvalue" local fun = function() func = testupvalue.newCounter(); print(func()); print(func()); print(func()); func = testupvalue.newCounter(); print(func()); print(func()); print(func()); --[[ 輸出結果為: 1 2 3 1 2 3 --]] end xpcall(fun,print) os.execute("pause")