lua模組註冊
Lua自帶的模組並不多,好處就是Lua足夠的小,畢竟它的設計目標是定位成一個嵌入式的輕量級語言的.
相關的函式index2adr
static TValue *index2adr (lua_State *L, int idx) { if (idx > 0) { TValue *o = L->base + (idx - 1); api_check(L, idx <= L->ci->top - L->base); if (o >= L->top) return cast(TValue *, luaO_nilobject); else return o; } else if (idx > LUA_REGISTRYINDEX) { api_check(L, idx != 0 && -idx <= L->top - L->base); return L->top + idx; } else switch (idx) { /* pseudo-indices */ case LUA_REGISTRYINDEX: return registry(L); case LUA_ENVIRONINDEX: { Closure *func = curr_func(L); sethvalue(L, &L->env, func->c.env); return &L->env; } case LUA_GLOBALSINDEX: return gt(L); default: { Closure *func = curr_func(L); idx = LUA_GLOBALSINDEX - idx; return (idx <= func->c.nupvalues) ? &func->c.upvalue[idx-1] : cast(TValue *, luaO_nilobject); } } }
一個Lua函式棧由兩個指標base和top來指定,base指向函式棧底,top則指向棧頂.
回到index2addr函式中,幾種情況:
- 如果索引為正,則從函式棧底為起始位置向上查詢資料
- 如果索引為負,則從函式棧頂為起始位置向下查詢資料
- 緊跟著是幾種特殊的索引值,都定義了非常大的資料,由於Lua棧限定了函式的棧尺寸,所以不會有那麼大的索引,大可放心使用.
索引值為LUA_REGISTRYINDEX時,則返回的是全域性資料global_state的l_registry表;如果索引值為LUA_GLOBALSINDEX,則返回該Lua_State的l_gt表.
lua模組註冊
Lua內部所有模組的註冊都在linit.c的函式luaL_openlibs中提供.可以看到的是,它依次訪問一個數組,陣列中定義了每個模組的模組名及相應的模組註冊函式,依次呼叫函式就完成了模組的註冊.
static const luaL_Reg lualibs[] = { {"", luaopen_base}, {LUA_LOADLIBNAME, luaopen_package}, {LUA_TABLIBNAME, luaopen_table}, {LUA_IOLIBNAME, luaopen_io}, {LUA_OSLIBNAME, luaopen_os}, {LUA_STRLIBNAME, luaopen_string}, {LUA_MATHLIBNAME, luaopen_math}, {LUA_DBLIBNAME, luaopen_debug}, {NULL, NULL} }; LUALIB_API void luaL_openlibs (lua_State *L) { const luaL_Reg *lib = lualibs; for (; lib->func; lib++) { lua_pushcfunction(L, lib->func); lua_pushstring(L, lib->name); lua_call(L, 1, 0); } }
我沒有詳細的檢視每個模組的註冊函式,不過還是以最簡單的例子來講解,就是最常用的print函式.
由於這個函式沒有字首,因此的它所在的模組是”",也就是一個空字串,因此它是在base模組中註冊的,呼叫的註冊函式是luaopen_base.
緊跟著繼續看luaopen_base內部呼叫的第一個函式base_open:
static void base_open (lua_State *L) {
/* set global _G */
lua_pushvalue(L, LUA_GLOBALSINDEX);
lua_setglobal(L, "_G");
/* open lib into global table */
luaL_register(L, "_G", base_funcs);
// ....
}
首先來看最前面的兩句:
/* set global _G */
lua_pushvalue(L, LUA_GLOBALSINDEX);
lua_setglobal(L, "_G");
這兩句首先將LUA_GLOBALSINDEX對應的值壓入棧中,其次呼叫”lua_setglobal(L, “_G”);”,這句程式碼的意思是在Lua_state的l_gt表中,當查詢”_G”時,查詢到的是索引值為LUA_GLOBALSINDEX的表.如果覺得有點繞,可以簡單這個理解,在Lua中的G表,也就是全域性表,滿足這個等式”_G = _G["_G"]“,也就是這個叫”_G”的表,內部有一個key為”_G”的表是指向自己的.懷疑這個結論的,可以在Lua命令列中執行print(_G)和print(_G["_G"])看看輸出結果是不是一致的.
Lua中要這麼處理的理由是:為了讓G表和處理其它表使用同樣的機制.查詢一個變數時,最終會一直查到G表中,這是很自然的事情;所以為了也能按照這個機制順利的查詢到自己,於是在G表中有一個同名成員指向自己.
好了,前面兩句的作用已經分析完畢.其結果有兩個:
- _G = _G["_G"]
- _G表的值壓入函式棧中方便了下面的呼叫.
繼續看下面的語句:
**luaL_register(L, “_G”, base_funcs);
它最終會將base_funcs中的函式註冊到G表中,但是裡面還有些細節需要看看的.**
LUALIB_API void luaI_openlib (lua_State *L, const char *libname,
const luaL_Reg *l, int nup) {
if (libname) {
int size = libsize(l);
/* check whether lib already exists */
luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1);
lua_getfield(L, -1, libname); /* get _LOADED[libname] */
if (!lua_istable(L, -1)) { /* not found? */
lua_pop(L, 1); /* remove previous result */
/* try global variable (and create one if it does not exist) */
if (luaL_findtable(L, LUA_GLOBALSINDEX, libname, size) != NULL)
luaL_error(L, "name conflict for module " LUA_QS, libname);
lua_pushvalue(L, -1);
lua_setfield(L, -3, libname); /* _LOADED[libname] = new table */
}
lua_remove(L, -2); /* remove _LOADED table */
lua_insert(L, -(nup+1)); /* move library table to below upvalues */
}
// ...
}
註冊這些函式之前,首先會到l_registry表的成員_LOADED表中查詢該庫,如果不存在則再在G表中查詢這個庫,不存在則建立一個表.因此,不管是lua中內部的庫或者是外部使用require引用的庫,都會走這個流程並最終在G表和l_registry["_LOADED"]中存放該庫的表.最後,再遍歷傳進來的函式指標陣列,完成庫函式的註冊.
比如,註冊os.print時,首先將print函式繫結在一個函式指標上,再去l_registry["_LOADED"]和G表中查詢該名為”os”的庫是否存在,不存在則建立一個表,即:
G["os"] = {}
緊跟著註冊print函式,即: G["os"]["print"] = 待註冊的函式指標.這樣,在呼叫lua程式碼os.print(1)時,首先根據”os”到G表中查詢對應的表,再在這個表中查詢”print”成員得到函式指標,最後完成函式的呼叫.
註冊外部模組
luaL_newlibtable 它僅僅是建立了一個table,然後把數組裡的函式放進去而已
luaL_setfuncs它把陣列l中的所有函式註冊入棧頂的table,並給所有函式綁上nup個upvalue
define luaL_newlibtable(L, l)
lua_createtble(L, 0, sizeof(l)/sizeof((l)[0]) - 1)
define luaL_newlib(L, l)
(luaL_newlibtable(L,l), luaL_setfuncs(L,l,0)
LUALIB_API void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup){
luaL_checkversion(L);
luaL_checkstack(L, nup, "too_many_upvalue");
for(; l->name != NULL; i++){/* fill the table with given functions*/
int i;
for(i = 0; i < nup; i++)/copy upvalues to the top/
lua_pushvalue(L, -nup);
lua_pushclosure(L, l->func, nup);/closure with those upvalues/
lua_setfield(L, -(nup + 2), l->name);
}
lua_pop(L, nup);/remove upvalues/
}