第一版是為 Lua 5.0 編寫的。儘管在很大程度上仍然適用於後續版本,但還是存在一些差異。
第四版針對 Lua 5.3,可在 Amazon 和其他書店購買。
購買此書,您還將幫助 支持 Lua 項目


26.1 – C 函數

作為第一個示例,讓我們看看如何實現一個簡化版本的函數,該函數返回給定數字的正弦值(更專業的實現應檢查其參數是否為數字)

    static int l_sin (lua_State *L) {
      double d = lua_tonumber(L, 1);  /* get argument */
      lua_pushnumber(L, sin(d));  /* push result */
      return 1;  /* number of results */
    }
使用 Lua 註冊的任何函數都必須具有此相同原型,在 lua.h 中定義為 lua_CFunction
    typedef int (*lua_CFunction) (lua_State *L);
從 C 的角度來看,C 函數將 Lua 狀態作為其單個參數,並(在 C 中)返回一個整數,其中包含它正在返回的值的數量(在 Lua 中)。因此,函數不需要在推送其結果之前清除堆疊。在函數返回後,Lua 會自動移除結果下方堆疊中的任何內容。

在我們可以從 Lua 使用此函數之前,我們必須註冊它。我們使用 lua_pushcfunction 執行此神奇操作:它獲取一個指向 C 函數的指標,並創建一個類型為 "function" 的值來表示 Lua 中的此函數。測試 l_sin 的一種快速且簡便的方法是將其代碼直接放入 lua.c 檔案中,並在呼叫 lua_open 之後立即添加以下行

    lua_pushcfunction(l, l_sin);
    lua_setglobal(l, "mysin");
第一行推送一個函數類型值。第二行將其指定給全域變數 mysin。在進行這些修改後,您重新建置您的 Lua 可執行檔;然後您可以在 Lua 程式中使用新的函數 mysin。在下一節中,我們將討論將新的 C 函數與 Lua 連結的更好方法。

對於更專業的正弦函數,我們必須檢查其參數的類型。在此,輔助函式庫對我們有所幫助。luaL_checknumber 函數檢查給定的參數是否為數字:如果發生錯誤,它會擲出一個提供資訊的錯誤訊息;否則,它會返回該數字。我們函數中的修改很小

    static int l_sin (lua_State *L) {
      double d = luaL_checknumber(L, 1);
      lua_pushnumber(L, sin(d));
      return 1;  /* number of results */
    }
使用上述定義,如果您呼叫 mysin('a'),您會收到訊息
    bad argument #1 to `mysin' (number expected, got string)
請注意 luaL_checknumber 如何自動使用參數編號 (1)、函數名稱 ("mysin")、預期的參數類型 ("number") 和實際參數類型 ("string") 填寫訊息。

作為一個更複雜的範例,讓我們撰寫一個會傳回給定目錄內容的函式。Lua 沒有在標準函式庫中提供這個函式,因為 ANSI C 沒有這個工作的函式。這裡,我們會假設我們有一個符合 POSIX 的系統。我們的函式 dir,會取得一個字串作為目錄路徑的引數,並傳回一個陣列,其中包含目錄項目。例如,一個呼叫 dir("/home/lua") 可能會傳回表格 {".", "..", "src", "bin", "lib"}。如果發生錯誤,這個函式會傳回 nil 加上一個包含錯誤訊息的字串。

    #include <dirent.h>
    #include <errno.h>
    
    static int l_dir (lua_State *L) {
      DIR *dir;
      struct dirent *entry;
      int i;
      const char *path = luaL_checkstring(L, 1);
    
      /* open directory */
      dir = opendir(path);
      if (dir == NULL) {  /* error opening the directory? */
        lua_pushnil(L);  /* return nil and ... */
        lua_pushstring(L, strerror(errno));  /* error message */
        return 2;  /* number of results */
      }
    
      /* create result table */
      lua_newtable(L);
      i = 1;
      while ((entry = readdir(dir)) != NULL) {
        lua_pushnumber(L, i++);  /* push key */
        lua_pushstring(L, entry->d_name);  /* push value */
        lua_settable(L, -3);
      }
    
      closedir(dir);
      return 1;  /* table is already on top */
    }
輔助函式庫中的 luaL_checkstring 函式,是字串的 luaL_checknumber 等效函式。

(在極端情況下,l_dir 的那個實作可能會造成一個小的記憶體外洩。它呼叫的 Lua 函式中有三個可能會因為記憶體不足而失敗:lua_newtablelua_pushstringlua_settable。如果這些呼叫中的任何一個失敗,它會引發一個錯誤並中斷 l_dir,因此不會呼叫 closedir。如同我們先前討論的,在大部分的程式中這不是一個大問題:如果程式用盡記憶體,它能做的最好的事就是關閉。不過,在 第 29 章 中,我們會看到一個避免這個問題的目錄函式替代實作。)