在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指向同一個表,這樣這個表就變成了一個所有函數共享數據的地方。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM