[Lua]在C函數中儲存狀態--註冊表,環境表,upvalue

來源:互聯網
上載者:User

標籤:

什麼叫做在C函數中儲存狀態?比如你現在使用Lua調用了C函數Func1,但是Func1中有一些資料在調用完以後儲存下來,供以後使用。而這些資料就是所謂的狀態,也就是我們需要儲存的東東。有人就會說了,Lua調用C時,把所有的需要儲存的狀態都返回到Lua中,當調用下一個函數時,將需要的狀態當做參數再傳進去,不錯,是一個辦法,但是很麻煩。方法一:註冊表;方法二:環境;方法三:upvalue。
註冊表是一個全域的table,它只能被C代碼訪問。通常,可以用它來儲存那種需要在幾個模組中共用的資料;
但是,如果需要儲存一個模組的私人資料,那麼應該使用環境,與Lua函數一樣,每個C函數都有自己的環境table,通常情況下,一個模組內的所有函數共用同一個環境table,由此它們可以共用資料。
最後,C函數也可以擁有upvalue,upvalue是一種與特定函數相關聯的Lua值。

註冊表註冊表總是位於一個“虛擬索引”上,這個索引值由LUA_REGISTRYINDEX定義。虛擬索引就像是一個棧中的索引,但它所關聯的值不在棧中;所完這句話,你想到了什嗎?C++中,使用new開闢空間,這個空間是在堆上開闢的,而指向這個堆的變數卻是存放在棧上的。虛擬索引和這個意思差不多。Lua API中的大多數函數都能接受虛擬索引,但像lua_remove和lua_insert這種操作棧本身的函數卻只能使用普通索引。
註冊表是一個普通的Lua  table,可以使用任何Lua值(nil除外)來索引它。
#include<iostream>using namespace std;#include<lua.hpp>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;}
環境表從5.1開始,在Lua中註冊的所有C函數都有自己的環境table。一個函數可以像訪問註冊表那樣,通過一個虛擬索引來訪問它的環境table。環境table的虛擬索引是LUA_ENVIRONINDEX。
這種使用環境的方法與在Lua模組中使用環境的方法差不多,都是先為模組建立一個新的table,然後使模組中的所有函數都共用這個table。只不過,在Lua中使用了一個setfenv函數,而在C模組中,只不過是設定table為LUA_ENVIRONINDEX

#include<iostream>using namespace std;#include<lua.hpp>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時,所有建立的函數都會繼承當前環境。}
test.lua檔案內容
require "testenv"local fun1 = function()    local var = "Hello,world!!!"    testenv.setValue(var)    print(testenv.getValue()) --輸出Hello,world!!!endxpcall(fun,print)os.execute("pause")
上面先將值設定到模組環境table中。然後再從中取出來。這個和上面說的註冊表有很多的相似之處。儘管可能使用環境來代替註冊表,但是如果沒有在不同模組之間共用資料的需要,就儘可能的不要使用註冊表;使用環境建立的引用,只是在本模組中可見,這樣縮小了資料的使用範圍了,減小了資料被錯改的可能,增加了資料的安全性。upvalueupvalue是和特定函數關聯的,我們可以將其簡單的理解為函數內的靜態變數。 
註冊表提供了全域變數的儲存,環境提供了模組變數的儲存,而upvalue機制則實現了一種類似於C語言中靜態變數的機制。而這種upvalue機制,可以讓我們定義一個只在特定的函數中可見的變數。每當在Lua中建立一個函數時,都可以將任意數量的upvalue與這個函數相關聯。每個upvalue都可以儲存一個Lua值。以後,在調用這個函數時,就可以通過虛擬索引來訪問這些upvalue了。將這種C函數與upvalue的關聯稱為closure(也叫閉包,多麼熟悉的名字)。一個C closure類似於Lua closure。closure可以用同一個函數代碼來建立多個closure,每個closure可以擁有不同的upvalue。
#include<iostream>using namespace std;#include<lua.hpp>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;}
test.lua檔案內容
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    --]]endxpcall(fun,print)os.execute("pause")

[Lua]在C函數中儲存狀態--註冊表,環境表,upvalue

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.