http://blog.chinaunix.net/uid-552961-id-2736410.html
lua中的require機制
為了方便代碼管理,通常會把lua代碼分成不同的模塊,然后在通過require函數(shù)把它們加載進來。
現(xiàn)在看看lua的require的處理流程。
1、require機制相關(guān)的數(shù)據(jù)和函數(shù)
package.path:保存加載外部模塊(lua中"模塊"和"文件"這兩個概念的分界比較含糊,因為這個值在不同的時刻會扮演不同的角色)的搜索 路徑,這種路徑是"模板式的路徑",它里面會包含可替代符號"?",這個符號會被替換,然后lua查找這個文件是否存在,如果存在就會調(diào)用其中特定的接 口。典型的值為:
"./?.lua;./?.lc;/usr/local/?/init.lua"
如果lua代碼中調(diào)用:require("hello.world")
那么lua會依次查找:
./hello/world.lua ==>這里"hello.world"變成了"hello/world",并替換了模型"./?.lua"
./hello/world.lc
.....
(這種處理方式和python類似,只不過不需要__init__.py,也有調(diào)用python中的__init__.py)
package.path在虛擬機啟動的時候設(shè)置,如果存在環(huán)境變量LUA_PATH,那么就用該環(huán)境變量作為
它的值,并把這個環(huán)境變量中的";;"替換為luaconf.h中定義的默認(rèn)值,如果不存在該變量就直接使用
luaconf.h定義的默認(rèn)值
package.cpath:作用和packag.path一樣,但它是用于加載第三方c庫的。它的初始值可以通過環(huán)境變量
LUA_CPATH來設(shè)置
package.loadlib(libname, func):相當(dāng)與手工打開c庫libname, 并導(dǎo)出函數(shù)func返回,loadlib其實是ll_loadlib
2.require的處理流程:
require(modelname)
require(在lua中它是ll_require函數(shù))的查找順序如下:
a.首先在package.loaded查找modelname,如果該模塊已經(jīng)存在,就直接返回它的值
b.在package.preload查找modelname, 如果preload存在,那么就把它作為loader,調(diào)用loader(L)
c.根據(jù)package.path的模式查找lua庫modelname,這個庫是通過module函數(shù)定義的,對于頂層的lua庫,文件名和庫名是一 樣的而且不需要調(diào)用顯式地在lua文件中調(diào)用module函數(shù)(在ll_require函數(shù)中可以看到處理方式),也就是說lua會根據(jù)lua文件直接完 成一個loader的初始化過程。
d.根據(jù)package.cpath查找c庫,這個庫是符合lua的一些規(guī)范的(export具有一定特征的函數(shù)接口),lua先已動態(tài)的方式加載該c庫,然后在庫中查找并調(diào)用相應(yīng)名字的接口,例如:luaopen_hello_world
e.已第一個"."為分割,將模塊名劃分為:(main, sub)的形式,根據(jù)package.cpath查找main,如果存在,就加載該庫并查詢相應(yīng)的接口:luaopen_main_sub,例如:先查找 hello庫,并查詢luaopen_hello_world接口
f.得到loder后,用modname作為唯一的參數(shù)調(diào)用該loader函數(shù)。當(dāng)然參數(shù)是通過lua的棧傳遞的,所以loader的原型必須符合lua的規(guī)范:int LUA_FUNC(lua_State *L)
ll_require會將這個loader的返回值符給package.loaded[modelname],如果loader不返回值同時 package.loaded[modelname]不存在時, ll_require就會把package.loaded[modelname]設(shè)為true。最后ll_reuqire把package.loaded [modelname]返回給調(diào)用者。
3.module的處理流程
module(name, cb1, cb2, ...)
a.如果package.loaded[name]是一個table,那么就把這個table作為一個mod
b.如果全局變量name是一個table,就把這個全局變量作為一個mod
c.創(chuàng)建table:t = {[name]=package.loaded[name], ["_NAME"]=name, ["_M"]=t, ["_PACKAGE"]=*name*(刪除了最后的".XXXX"部分)}. 如果name是一個以點分割的串,那么得到的mod類似這個樣子:
hello.world==> {["hello"]={["world"]={XXXXXXX}}}
d.依次調(diào)用cbs:
cb1(mod), cb2(mod),...
e.將當(dāng)前模塊的環(huán)境設(shè)置為mod,同時把package.loaded[name] = mod
清楚了lua關(guān)于模塊的處理,就比較容易理解寫lua擴展的細(xì)節(jié)了^_^。