標籤:style blog color 使用 os strong 資料 問題
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指向同一個表,這樣這個表就變成了一個所有函數共用資料的地方。