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/
}
lua模塊註冊