Lua4.0 參考手冊(六)5.8-5.14

(接上篇)
-------------------
5.8 執行 Lua 代碼
-------------------
一個宿主程序能夠執行寫在文件中或在字符串中的 Lua 塊,使用下面的函數:
數組

    int lua_dofile (lua_State *L, const char *filename);
    int lua_dostring (lua_State *L, const char *string);
    int lua_dobuffer (lua_State *L, const char *buff,
                      size_t size, const char *name);

這些函數返回 0 表示成功,或者失敗時返回一個錯誤碼:
> LUA_ERRRUN - 執行模塊時出錯。
> LUA_ERRSYNTAX - 預編譯時語法錯誤。
> LUA_ERRMEM - 內存分配錯誤。對於此類錯誤,Lua 不會調用 _ERRORMESSAGE (參見 4.7 節)。
> LUA_ERRERR - 執行 _ERRORMESSAGE 時出錯。對於此類錯誤,Lua 不會再次調用 _ERRORMESSAGE ,以避免死循環。
> LUA_ERRFILE - 打開文件錯誤(只針對 lua_dofile)。在這種狀況下,你可能想要檢查 errno,調用 strerror,或者調用 perror 去告訴用戶哪裏出了問題。

這些常量定義在 lua.h 中。

當經過 NULL 參數調用時,lua_dofile 執行標準輸入流。lua_dofile 和 lua_dobuffer 均可以執行預編譯的塊。它們自動檢測出塊是文本或者二進制,並相應地加載它(參見程序 luac)。lua_dostring 只執行文本格式的源代碼。

lua_dobuffer 的第三個參數是塊的名字,它用在錯誤信息和調試信息中。若是名字是 NULL,Lua 會給塊一個默認的名字。

這些函數把塊最終返回的結果壓棧。一個塊能夠返回任意數量的值;Lua 保證這些值能夠存放到棧空間中,可是調用以後就須要你本身維護棧空間了。若是你須要壓棧其它的元素在調用這些函數以後,而且你想安全地使用棧空間,你必需要麼經過 lua_stackspace 來檢查棧空間,要求從棧同移除元素(若是你不須要它們的話)。例如,下面的代碼加載一個文件中的塊而且丟棄全部的這個塊返回的結果,把棧恢復爲調用之間的狀態:
安全

   {
    int oldtop = lua_gettop(L);
    lua_dofile(L, filename);
    lua_settop(L, oldtop);
   }

-------------------
5.9 操縱 Lua 中的全局變量
-------------------
可使用下面的函數讀取 Lua 全局變量的值:
閉包

    void lua_getglobal (lua_State *L, const char *varname);

它將會把給定變量的值壓棧。在 Lua 中,這個函數可能觸發一個標籤方法 ``getglobal''(參見 4.8 節)。爲了讀取全局變量真正的值,而不喚起任何標籤方法,在全局表上使用 lua_rawget(參見下面)。

保存一個值到全局變量,調用
函數

    void lua_setglobal (lua_State *L, const char *varname);

它出棧須要保存到給定變量的值。在 Lua 中,這個函數可能會觸發一個標籤方法 ``setglobal'' (參見 4.8 節)。爲了設置全局變量的真正的值,而不喚起任何標籤方法,在全局表上使用 lua_rawset (參見下面)。

全部的全局變量都保存在一個普通的表中。你能夠得到這個表經過調用
lua

    void lua_getglobals (lua_State *L);

它把當前的全局變量的表壓棧。爲設置其它的表做爲全局變量的表,調用
spa

    void lua_setglobals (lua_State *L);

將要使用的表出棧。
-------------------
5.10 操縱 Lua 中的表
-------------------
Lua 表也能夠經過 API 來操做。
爲讀取表中的值,表必須在棧的某個地方。在這種狀況下,調用
指針

    void lua_gettable (lua_State *L, int index);

這裏 index 指定表。lua_gettable 出棧一個 key,而且返回(在棧上)表在 key 處的內容。在 Lua 中,這個操做可能會觸發一個標籤方法 ``gettable'' 。爲了得到表的 key 的真正值,而不喚起任何標籤方法,使用 raw 版本的:
調試

    void lua_rawget (lua_State *L, int index);

爲保存一個值到表中,表已經在棧的某個地方了,你壓棧 key 和值(以這種順序),而後調用
code

    void lua_settable (lua_State *L, int index);

這裏的 index 指定表。lua_settable 出棧 key 和 value。在 Lua 中,這個操做可能會觸發一個標籤方法 ``settable'' 。爲了設置任意表的 index 的真正值,而不喚起任何標籤方法,使用 raw 版本的:
對象

    void lua_rawset (lua_State *L, int index);

最後,函數

    void lua_newtable (lua_State *L);

建立一個新的空表並壓棧。

-------------------
5.11 把表做爲數組使用
-------------------
有 API 函數可使用 Lua 表做爲數組,也就是說,表的索引都有數值:

    void lua_rawgeti (lua_State *L, int index, int n);
    void lua_rawseti (lua_State *L, int index, int n);
    int lua_getn (lua_State *L, int index);

lua_rawgeti 得到在棧的 index 處表的第 n 個元素。

lua_rawseti 設置在棧的 index 處表的第 n 個元素爲棧頂的值。

lua_getn 返回在棧 index 處表的元素個數。這個數值是表中字段 n 的值,若是它有一個數值型值,或者表中最大的數值型索引不是一個 nil 值。

-------------------
5.12 調用 Lua 函數
-------------------
定義在 Lua 中的函數(或者註冊到 Lua 的 C 函數)能夠從宿主程序調用。這採用以下協議:首先,被調用的函數壓棧;而後,函數的參數以直接順序壓棧(參見 5.5 節),即,第一個參數先壓棧。最後,函數被調用經過:

    int lua_call (lua_State *L, int nargs, int nresults);

這個函數返回和 lua_dostring (參見 5.8 節)一樣的錯誤碼。若是你想傳播這個錯誤,而不是返回一個錯誤碼,使用

    void lua_rawcall (lua_State *L, int nargs, int nresults);

在這兩個函數中,nargs 是你壓棧的參數的個數。全部的參數和函數值都從棧中彈出,函數的返回值壓棧。函數的返回值被調整(參見 4.3 節)爲 nresults,除非 nresults 是 LUA_MULTRET。在這種狀況下,全部的函數返回值都壓棧。函數的返回值以直接順序壓棧(第一個返回值最早壓棧),因此調用以後最後一個返回值在棧頂。
下面的例子顯示宿主程序如何作到和 Lua 代碼等價的:

    a,b = f("how", t.x, 4)

在 C 中:

    lua_getglobal(L, "t"); /* global `t' (for later use) */
    lua_getglobal(L, "f"); /* function to be called */
    lua_pushstring(L, "how"); /* 1st argument */
    lua_pushstring(L, "x"); /* push the string `x' */
    lua_gettable(L, -4); /* push result of t.x (2nd arg) */
    lua_pushnumber(L, 4); /* 3rd argument */
    lua_call(L, 3, 2); /* call function with 3 arguments and 2 results */
    lua_setglobal(L, "b"); /* set global variable `b' */
    lua_setglobal(L, "a"); /* set global variable `a' */
    lua_pop(L, 1); /* remove `t' from the stack */

注意,上面的代碼是平衡的:在它結束時,棧被回退爲它的原來配置。這被認爲是好的編譯實踐。

一些特別的 Lua 函數有它們本身的 C 接口。宿主程序能夠生成一個 Lua 錯誤經過調用函數

    void lua_error (lua_State *L, const char *message);

這個函數永遠不返回。若是 lua_error 被從 Lua 調用的 C 函數中調用,相應的 Lua 執行被終結,就如同 Lua 代碼中發生了一個錯誤。不然,整個宿主程序被一個 exit (EXIT_FAILURE)調用終止。在終結執行以前,message 被傳遞給錯誤處理函數 _ERRORMESSAGE (參見 4.7 節)。若是 message 爲 NULL, _ERRORMESSAGE 便不會被調用。

標籤方法能夠經過下面的函數改變

    void lua_settagmethod (lua_State *L, int tag, const char *event);

第二個參數是標籤,第三個參數是事件名字(參見 4.8 節);新的方法從棧上彈出。爲得到當前的
標籤方法的值,使用函數

    void lua_gettagmethod (lua_State *L, int tag, const char *event);

能夠拷貝全部的標籤方法從一個標籤到另外一個:

    int lua_copytagmethods (lua_State *L, int tagto, int tagfrom);

這個函數返回 tagto。
你能夠遍歷一個表經過函數

    int lua_next (lua_State *L, int index);

這裏 index 指定被遍歷的表。這個函數從棧中彈出一個 key,並從表中壓棧一個 key-value 對(也就是給定 key 的下一個鍵值對)。若是沒有元素了,函數返回 0 (不壓棧)。一個典型的調用以下:

    /* table is in the stack at index `t' */
    lua_pushnil(L); /* first key */
    while (lua_next(L, t) != 0) {
      /* `key' is at index -2 and `value' at index -1 */
      printf("%s - %s\n",
        lua_typename(L, lua_type(L, -2)), lua_typename(L, lua_type(L, -1)));
      lua_pop(L, 1); /* removes `value'; keeps `index' for next iteration */
    }

函數

    void lua_concat (lua_State *L, int n);

鏈接棧頂的 n 個值,彈出它們,而且壓棧結果;n 最小爲 2 。鏈接操做遵照 Lua 通用的語義(參見 4.5.5 節)。

-------------------
5.13 定義 C 函數
-------------------
註冊 C 函數到 Lua ,用下面的宏:

    #define lua_register(L, n, f) (lua_pushcfunction(L, f), lua_setglobal(L, n))
    /* const char *n; */
    /* lua_CFunction f; */

它接受函數在 Lua 中的名字,和一個函數指針。這個指針的類型必須爲 lua_CFunction,其定義爲:

    typedef int (*lua_CFunction) (lua_State *L);

也就是一個 Lua 環境參數一個整型返回值的函數指針。

爲了和 Lua 正確的交互,C 函數必須遵照下面的協議,這個協議規定了參數和返回值傳遞的方法:C 函數在棧裏獲得它從 Lua 得到的參數,以直接順序(第一個參數最早壓棧)。爲了返回值到 Lua, C 函數能夠把返回值順序壓棧(第一個返回值最早壓棧),而且返回結果的個數。就像 Lua 函數同樣,一個由 Lua 調用的 C 函數也能夠返回多個值。

做爲一個例子,下面的函數接受一個可變個數的數值型參數而且返回它們的平均數和總數:

    static int foo (lua_State *L) {
      int n = lua_gettop(L); /* number of arguments */
      double sum = 0;
      int i;
      for (i = 1; i <= n; i++) {
        if (!lua_isnumber(L, i))
          lua_error(L, "incorrect argument to function `average'");
        sum += lua_tonumber(L, i);
      }
      lua_pushnumber(L, sum/n); /* first result */
      lua_pushnumber(L, sum); /* second result */
      return 2; /* number of results */
    }

函數能夠做爲 'average' 註冊到 Lua ,經過調用

    lua_register(L, "average", foo);

當新建一個 C 函數時,能夠爲它關聯一些 upvalue (參見 4.6 節),從而新建一個 C 閉包,當函數被調用時這些值被傳遞給它,做爲普通的參數。爲了爲了一個 C 函數關聯 upvalue,首先這些值應該壓棧。而後函數

    void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);

被用來壓棧 C 函數,參數 n 說明應該有多少個 upvalue 被關聯到函數(這些 upvalue 從棧上彈出);事實上,宏 pushcfunction 被定義爲參數 n 爲 0 的 lua_pushcclosure 。而後,不管什麼時候 C 函數被調用時,這些 upvalue 被插入函數做爲最後的參數,在函數調用的實際參數以後。這使得程序容易得到 upvalue 而沒必要知道函數接受多少個參數(回憶一下,Lua 中的函數能夠接受任意數量的參數):第 i 個 upvalue 在棧裏的索引爲 i-(n+1),這裏 n 爲 upvalue 的個數。
更多的 C 函數和閉包的例子,參見官方發佈版中的文件 lbaselib.c, liolib.c, lmathlib.c, 和 lstrlib.c 。

-------------------
5.14 Lua 對象的引用
-------------------
若是一個 C 代碼須要在一個 C 函數的生存期以外保持一個 Lua 值的話, 它必須新建那個值的引用(reference)。下面的函數能夠管理這種引用:

    int lua_ref (lua_State *L, int lock);
    int lua_getref (lua_State *L, int ref);
    void lua_unref (lua_State *L, int ref);

lua_ref 從棧頂彈出一個值,新建一個它的引用,並返回這個引用。一個 nil 值的引用永遠爲 LUA_REFNIL。(lua.h 也定義了一個不一樣於其它的有效引用的常量 LUA_NOREF 。)若是 lock 不是 0 的話,對象就被鎖定:這意味着這個對象將不會被垃圾回收。沒有鎖定的引用可被垃圾回收。

當 C 中須要引用對象的時候,調用 lua_getref 把那個對象壓棧;若是對象已經被垃圾回收了,lua_getref 返回 0 (不壓棧任何東西)。

當一個引用不須要了,應該使用 lua_unref 調用來釋放它。

註冊
當 Lua 啓動時,它在位置 LUA_REFREGISTRY 注意一個表。它能夠經過宏來引用

  #define lua_getregistry(L) lua_getref(L, LUA_REFREGISTRY)

這個表能夠被 C 庫做爲一個註冊機制使用。任何 C 庫能夠在這個表中保存數據,只要它使用的 key 不一樣於其它的庫。(未完待續)

相關文章
相關標籤/搜索