(接上篇)
--------------------------------------
5 API
--------------------------------------
這節描述 Lua 的 API, 也就是宿主程序和 Lua 交互的一組 C 函數。全部的 API 函數和相關聯的類型及常量都在文件 lua.h 中聲明。
全部的 API 提供的功能均可以由宏來替代,即便當咱們使用術語函數(``function'')。由於這樣的宏只使用它的參數一次,因此不會產生隱蔽的反作用。
-------------------
5.1 狀態(State)
-------------------
Lua 庫是徹底可重入的:它沒有使用任何全局變量。Lua 解釋器的完整狀態(全局變量,棧,標籤方法,等)保存在一個動態分配的 lua_State 類型中。這個狀態必須做爲第一個參數傳遞給庫中的每個函數(除了下面說的 lua_open)。
在調用任何 API 函數以前,你必須新建一個狀態,經過調用
lua_State *lua_open (int stacksize);
這個函數惟一的參數是解釋器的棧尺寸。(每一個函數調用的每一個參數須要一個棧位置,局部變量,臨時值,再加一個用作標記的位置。棧必須有額外的 20 個可用位置。對於很小的實現,沒有遞歸函數,棧的尺寸爲 100 可能就足夠了。)若是 stacksize 是 0 ,那麼默認的尺寸 1024 會被使用。
爲釋放一個由 lua_open 建立的狀態,調用
void lua_close (lua_State *L);
這個函數銷燬給定的 Lua 環境中的全部的對象(調用相應的垃圾回收標籤方法,若是有的話)而且釋放全部的這個狀態使用的動態內存。一般,你沒必要調用這個函數,由於當你的程序結束時全部的資源會被天然釋放。另外一方面,長時間運行的程序,如守護程序或一個 web 服務器程序,可能須要在狀態不須要的時候釋放它,以免內存增加過大。
除了 lua_open,全部的 Lua API 中的函數都須要一個狀態做爲它們的第一個參數。
-------------------
5.2 棧和索引
-------------------
Lua 用一個棧和 C 之間傳遞數據。棧中的每個元素表明一個 Lua 值(nil,數值,字符串,等)。
爲了方便起見,大多數 API 中的 query 操做並不需嚴格遵照棧的使用規則。反而,它們能夠經過使用一個索引來引用任何棧中的元素。一個正數索引表明一個絕對的棧位置(始於 1 ,不像 C 中從 0 開始)。一個負數索引表明一個從棧頂開始的偏移量。更具體地說,若是棧有 n 個元素,索引 1 表明第一個元素(即,第一個壓棧的元素),索引 n 表明最後一個元素;索引 -1 一樣表明最後一個元素(即,棧頂的元素),索引 -n 表明第一個元素。咱們說一個索引是有效的若是它位於 1 到棧頂之間(即,若是 1 <= abs(index) <= top)。
在任什麼時候間,你能夠得到棧頂元素的索引經過調用
int lua_gettop (lua_State *L);
由於索引開始於 1,lua_gettop 的結果等於棧中元素的個數(因此 0 表明一個空棧)。
當你和 Lua API 交互的時候,你有責任去控制棧的溢出。
函數
int lua_stackspace (lua_State *L);
返回棧中依然可用的位置個數。每當 Lua 調用 C,它保證最少有 LUA_MINSTACK 個位置可用。LUA_MINSTACK 在 lua.h 中定義,最小爲 16,因此你須要擔憂棧的空間僅當你的代碼中有循環壓棧的時候。
大部分 query 函數接受任何位於可用棧空間中的值爲索引。這樣的索引被稱爲可接受索引。更正式的講,咱們能夠定義一個可接受的索引以下
(index < 0 && abs(index) <= top) || (index > 0 && index <= top + stackspace)
注意 0 不是一個可接受的索引。
-------------------
5.3 棧的操縱
-------------------
API 爲基本的棧的操縱提供了下列函數
void lua_settop (lua_State *L, int index);
void lua_pushvalue (lua_State *L, int index);
void lua_remove (lua_State *L, int index);
void lua_insert (lua_State *L, int index);
lua_settop 接受任何可接受的索引,或者 0,而且設置棧頂爲該索引。若是新的棧頂比老的大,那麼新的元素被填充爲 nil。若是索引爲 0,那麼移除全部棧的元素。一個有用 API 中的宏定義是
#define lua_pop(L,n) lua_settop(L, -(n)-1)
它把棧頂的 n 個元素出棧。
lua_pushvalue 壓棧給定索引處的元素的拷貝。lua_remove 移除給定位置的元素,下移全部的該位置以上的元素去填充空白。lua_insert 把棧頂的元素移動到給定的位置,並把給定位置之上的位置上移以開闢空間。這些函數只接受有效的索引。做爲一個例子,若是棧開始時是 10 20 30 40 50 (從棧底到棧頂),那麼
lua_pushvalue(L, 3) --> 10 20 30 40 50 30
lua_pushvalue(L, -1) --> 10 20 30 40 50 30 30
lua_remove(L, -3) --> 10 20 30 40 30 30
lua_remove(L, 6) --> 10 20 30 40 30
lua_insert(L, 1) --> 30 10 20 30 40
lua_insert(L, -1) --> 30 10 20 30 40 (no effect)
lua_settop(L, -3) --> 30 10 20
lua_settop(L, 6) --> 30 10 20 nil nil nil
-------------------
5.4 查詢棧
-------------------
爲了檢查一個棧元素的類型,可使用下面的函數:
int lua_type (lua_State *L, int index);
int lua_tag (lua_State *L, int index);
int lua_isnil (lua_State *L, int index);
int lua_isnumber (lua_State *L, int index);
int lua_isstring (lua_State *L, int index);
int lua_istable (lua_State *L, int index);
int lua_isfunction (lua_State *L, int index);
int lua_iscfunction (lua_State *L, int index);
int lua_isuserdata (lua_State *L, int index);
這些函數可使用任何可接受的索引調用。
lua_type 根據給定對象的類型返回下面的常量之一:LUA_TNIL, LUA_TNUMBER, LUA_TSTRING, LUA_TTABLE, LUA_TFUNCTION, LUA_TUSERDATA。若是索引是無效的(即,若是棧的位置是空),lua_type 返回 LUA_TNONE。這個常量能夠轉換成字符串用
const char *lua_typename (lua_State *L, int t);
這裏 t 是一個 lua_type 返回的類型。lua_typename 返回的字符串是:"nil", "number", "string", "table", "function", "userdata", 和 "no value"。
lua_tag 返回值的標籤,或者 LUA_NOTAG 當索引無效時。
函數 lua_is* 返回 1 若是對象的類型的給定的類型兼容的話,不然 0 。對於無效的索引它們老是返回 0 。lua_isnumber 接受數值和數值的字符串, lua_isstring 接受字符串和數字(參見 4.2 節),lua_isfunction 接受 Lua 函數和 C 函數。爲了區分 Lua 函數和 C 函數,你應該使用 lua_iscfunction。爲了區分數值和數值的字符串,你可使用 lua_type。
API 也有函數用來比較棧上的兩個值:
int lua_equal (lua_State *L, int index1, int index2);
int lua_lessthan (lua_State *L, int index1, int index2);
這些函數和它們在 Lua 中相對應的函數是等價的。特別地,lua_lessthan 等價於 lt_event 描述位於 4.8 節。兩個函數都返回 0 若是任意一個索引是無效的話。
爲把棧上的一個值轉換爲一個指定的 C 類型,你可使用下面的轉換函數:
double lua_tonumber (lua_State *L, int index);
const char *lua_tostring (lua_State *L, int index);
size_t lua_strlen (lua_State *L, int index);
lua_CFunction lua_tocfunction (lua_State *L, int index);
void *lua_touserdata (lua_State *L, int index);
這些函數能夠用任何可接受的索引調用。當用一個無效的索引調用時,它們的行爲像是給定的值有一個錯誤的類型同樣。
lua_tonumber 把給定索引處的值轉換爲一個浮點數字。這個值必須是一個數值或者是能夠轉換爲數值的字符串(參見 4.2 節);不然,返回 0。
lua_tostring 把一個 Lua 值轉換爲一個字符串(const char*).這個值必須是一個字符串或者一個數值;不然,函數返回 NULL。這個函數返回一個指向 Lua 環境中的字符串的指針。這些字符串老是有一個 0 ('\0')在最後一個字符的後面(像 C 中那樣),可是可能包含其它的 0 在它內部。若是你不知道一個字符串是否包含 0,你可使用 lua_strlen 去取得它的實際長度。由於 Lua 有垃圾回收,沒有保證由 lua_tostring 返回的指針是有效的在各自的值在從棧上移除以後。
lua_tocfunction 把棧上的值轉換爲 C 函數。這個值必須是一個 C 函數;不然,lua_tocfunction 返回 NULL。類型 lua_CFunction 在 5.13 節解釋。
lua_touserdata 把一個值轉換爲 void*。這個值的必須有類型 userdata;不然,返回 NULL。
-------------------
5.5 壓棧
-------------------
API 有下面的函數用來把 C 的值壓棧:
void lua_pushnumber (lua_State *L, double n);
void lua_pushlstring (lua_State *L, const char *s, size_t len);
void lua_pushstring (lua_State *L, const char *s);
void lua_pushusertag (lua_State *L, void *u, int tag);
void lua_pushnil (lua_State *L);
void lua_pushcfunction (lua_State *L, lua_CFunction f);
函數接受一個 C 值,把它轉換爲相應的 Lua 值,而且把結果壓棧。特別地,lua_pushlstring 和 lua_pushstring 生成一個給定字符串的內部拷貝。lua_pushstring 只能用於壓棧合適的 C 字符串(即,字符串由 0 結尾且內容沒有 0 );不然你應該使用更通用的 lua_pushlstring,它接受一個明確指定的尺寸。
-------------------
5.6 垃圾回收
-------------------
Lua 用兩個數值來控制它的垃圾回收。一個數值計算 Lua 使用的多少字節的動態內存,另外一個是一個臨界值。(這個 Lua 保持的內部的字節計數器並不徹底準確;他只是一個下限,一般有 10% 的是正確的值[這半句不知道如何譯])當字節值達到臨界值,Lua 執行一次垃圾回收,它回收全部的無用的對象的內存(即,Lua 中訪問不到的對象)。字節計數器被校訂,而且臨界值被重置爲字節計數器的 2 倍。
你可使用這兩個值的當前值經過使用下面的函數:
int lua_getgccount (lua_State *L);
int lua_getgcthreshold (lua_State *L);
都分別返回它們的值,單元爲千字節。你能夠改變臨界值用
void lua_setgcthreshold (lua_State *L, int newthreshold);
再次的,給定 newthreshold 值的單元爲千字節。當你調用這個函數時,Lua 設置新的臨界值並再次檢查它和字節計數器。若是臨界值小於字節計數器,Lua 將馬上執行垃圾回收;垃圾回收以後,一個新的臨界值會被設置根據前面的規則。
若是你想改變垃圾回收的這種可適應行爲,你能夠用 nil 使用垃圾回收的標籤方法並設置你本身的臨界值(標籤方法會在 Lua 重置臨界值以後調用)。
-------------------
5.7 用戶自定義數據和標籤
-------------------
由於 userdata 是對象,函數 lua_pushusertag 可能新建一個 userdata。若是 Lua 有一個給定值(void*)和標籤的 userdata,那麼 userdata 被壓棧。不然,一個新的 userdata 被建立,有給定的值和標籤。若是這個函數經過標籤 LUA_ANYTAG 來調用,那麼 Lua 將試圖去尋找任何有給定值的 userdata,無論它的標籤。若是沒有 userdata 有那個值,那麼一個新的被建立,標籤爲 0 。
userdata 能夠有不一樣的標籤,它的語義只有宿主程序知道。標籤由下面的函數建立:
int lua_newtag (lua_State *L);
函數 lua_settag 改變棧頂對象的標籤(並不彈出它):
void lua_settag (lua_State *L, int tag);
對象必須是一個 userdata 或者是一個表;給定的標籤必須是由 lua_newtag 建立的值。
(未完待續)web