第一版是針對 Lua 5.0 編寫的。雖然在很大程度上仍然適用於後續版本,但有些地方有所不同。
第四版針對 Lua 5.3,可在 Amazon 和其他書店購買。
購買書籍,您也協助 支援 Lua 專案。
![]() |
用 Lua 程式設計 | ![]() |
第四部分。C API 第 27 章。撰寫 C 函式的技巧 |
雖然註冊表實作全域變數,但upvalue 機制實作等同於 C 靜態變數,只能在特定函式內部可見。每次在 Lua 中建立新的 C 函式時,您可以將任意數量的 upvalue 與其關聯;每個 upvalue 可以儲存單一 Lua 值。稍後呼叫函式時,它可以使用偽索引自由存取任何 upvalue。
我們將 C 函式與其 upvalue 的關聯稱為閉包。請記住,在 Lua 程式碼中,閉包是使用外部函式的區域變數的函式。C 閉包是 Lua 閉包的 C 近似值。關於閉包的有趣事實之一是,您可以使用相同的函式程式碼建立不同的閉包,但具有不同的 upvalue。
為了查看簡單範例,讓我們在 C 中建立一個 newCounter
函式。(我們已經在 第 6.1 節 中的 Lua 中定義了這個函式。)這個函式是工廠函式:每次呼叫時,它會傳回新的計數器函式。雖然所有計數器共用相同的 C 程式碼,但每個計數器都會保留自己的獨立計數器。工廠函式如下所示
/* forward declaration */ static int counter (lua_State *L); int newCounter (lua_State *L) { lua_pushnumber(L, 0); lua_pushcclosure(L, &counter, 1); return 1; }此處的關鍵函數為
lua_pushcclosure
,它會建立一個新的閉包。它的第二個參數是基礎函數(範例中的 counter
),而第三個參數則是 upvalue 的數量(範例中的 1)。在建立新的閉包之前,我們必須將其 upvalue 的初始值推入堆疊中。在我們的範例中,我們將數字 0 推入堆疊中,作為單一 upvalue 的初始值。正如預期,lua_pushcclosure
會將新的閉包留在堆疊中,因此閉包已準備好作為 newCounter
的結果傳回。
現在,讓我們看看 counter
的定義
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 */ }在此,關鍵函數為
lua_upvalueindex
(實際上是個巨集),它會產生 upvalue 的偽索引。同樣地,這個偽索引就像任何堆疊索引,只不過它並不存在於堆疊中。表達式 lua_upvalueindex(1)
指涉函數第一個 upvalue 的索引。因此,函數 counter
中的 lua_tonumber
會將第一個(也是唯一一個)upvalue 的目前值作為數字擷取出來。接著,函數 counter
會將新值 ++val
推入堆疊中,複製一份,並使用其中一份副本以新值取代 upvalue。最後,它會傳回另一份副本作為其回傳值。
與 Lua 閉包不同,C 閉包無法共用 upvalue:每個閉包都有其自己的獨立設定。不過,我們可以設定不同函數的 upvalue,使其參考同一張表,讓這張表成為這些函數可以共用資料的共用位置。
版權所有 © 2003–2004 Roberto Ierusalimschy。保留所有權利。 | ![]() |