第一版是為 Lua 5.0 所寫。雖然在很大程度上仍適用於後續版本,但有些地方有所不同。
第四版針對 Lua 5.3,可在 Amazon 和其他書店買到。
購買本書,您也可以 支持 Lua 專案


26.2 – C 函式庫

Lua 函式庫是一個區塊,定義了幾個 Lua 函式,並將它們儲存在適當的地方,通常是作為表格中的項目。Lua 的 C 函式庫模仿了這種行為。除了定義其 C 函式之外,它還必須定義一個特殊函式,對應於 Lua 函式庫的主區塊。呼叫此函式後,它會註冊函式庫的所有 C 函式,並將它們儲存在適當的地方。就像 Lua 主區塊一樣,它也會初始化函式庫中需要初始化的其他任何內容。

Lua 會透過這個註冊程序「看到」C 函式。一旦 C 函式在 Lua 中表示並儲存後,Lua 程式就會透過直接參考其位址(也就是我們在註冊函式時提供給 Lua 的內容)來呼叫它。換句話說,Lua 在註冊函式後,不會依賴函式名稱、套件位置或可見性規則來呼叫函式。通常,C 函式庫只有一個公開(extern)函式,也就是開啟函式庫的函式。所有其他函式都可以是私有的,在 C 中宣告為 static

當您使用 C 函式擴充 Lua 時,最好將您的程式碼設計為 C 函式庫,即使您只想註冊一個 C 函式:遲早(通常是早)您會需要其他函式。一如往常,輔助函式庫提供了一個輔助函式來執行此工作。luaL_openlib 函式接收一個 C 函式清單及其各自的名稱,並將它們全部註冊在一個具有函式庫名稱的表格中。舉例來說,假設我們要建立一個包含我們之前定義的 l_dir 函式的函式庫。首先,我們必須定義函式庫函式

    static int l_dir (lua_State *L) {
       ...  /* as before */
    }
接下來,我們宣告一個陣列,其中包含所有函式及其各自的名稱。此陣列具有 luaL_reg 類型的元素,這是一個具有兩個欄位的結構:一個字串和一個函式指標。
    static const struct luaL_reg mylib [] = {
      {"dir", l_dir},
      {NULL, NULL}  /* sentinel */
    };
在我們的範例中,只有一個函式(l_dir)要宣告。請注意,陣列中的最後一對必須是 {NULL, NULL},以表示其結束。最後,我們使用 luaL_openlib 宣告一個主函式
    int luaopen_mylib (lua_State *L) {
      luaL_openlib(L, "mylib", mylib, 0);
      return 1;
    }
luaL_openlib 的第二個引數是函式庫名稱。此函式會建立(或重複使用)一個具有給定名稱的表格,並使用 mylib 陣列指定的 name-function 對來填入表格。luaL_openlib 函式也允許我們為函式庫中的所有函式註冊常見的上值。目前,我們不使用上值,因此呼叫中的最後一個引數為零。luaL_openlib 在傳回時會在堆疊中留下它用來開啟函式庫的表格。luaopen_mylib 函式傳回 1 以將此值傳回 Lua。(與 Lua 函式庫一樣,此傳回是可選的,因為函式庫已經指定給一個全域變數。同樣地,就像在 Lua 函式庫中一樣,它不會花費任何成本,而且偶爾可能會很有用。)

完成函式庫後,我們必須將它連結到直譯器。如果你的 Lua 直譯器支援此功能,最方便的方法是使用動態連結功能。(請記住 第 8.2 節 中關於動態連結的討論。)在這種情況下,你必須使用你的程式碼建立一個動態函式庫(在 Windows 中為 .dll 檔案,在 Linux 中為 .so 檔案)。之後,你可以使用 loadlib 直接從 Lua 中載入你的函式庫。呼叫

    mylib = loadlib("fullname-of-your-library", "luaopen_mylib")
luaopen_mylib 函式轉換為 Lua 中的 C 函式,並將此函式指定給 mylib。(這說明了為什麼 luaopen_mylib 必須與任何其他 C 函式具有相同的原型。)接下來,呼叫 mylib() 會執行 luaopen_mylib,開啟函式庫。

如果你的直譯器不支援動態連結,則必須使用你的新函式庫重新編譯 Lua。除此之外,你需要一些方法來告訴獨立直譯器,它在開啟新狀態時應該開啟此函式庫。一些巨集可以簡化此任務。首先,你必須建立一個標頭檔(我們稱之為 mylib.h),其內容如下

    int luaopen_mylib (lua_State *L);
    
    #define LUA_EXTRALIBS { "mylib", luaopen_mylib },
第一行宣告開啟函式。下一行將巨集 LUA_EXTRALIBS 定義為直譯器在建立新狀態時呼叫的函式陣列中的新項目。(此陣列的類型為 struct luaL_reg[],因此我們需要在那裡放置一個名稱。)

若要將此標頭檔包含在直譯器中,你可以在編譯器選項中定義巨集 LUA_USERCONFIG。對於命令列編譯器,你通常必須新增一個選項,例如

    -DLUA_USERCONFIG=\"mylib.h\"
(反斜線保護殼層中的引號;當我們指定包含檔名稱時,在 C 中需要這些引號。)在整合開發環境中,你必須在專案設定中新增類似的內容。然後,當你重新編譯 lua.c 時,它會包含 mylib.h,因此會在要開啟的函式庫清單中使用 LUA_EXTRALIBS 的新定義。