1. 程式人生 > >在C 函數中保存狀態:registry、reference和upvalues

在C 函數中保存狀態:registry、reference和upvalues

targe 閉包 一個 兩個 table chang detail chan 這樣的

在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