環境: html
Lua5.1 LuaForWindowswindows
LuaForWindows的下載地址ide
http://files.luaforge.net/releases/luaforwindows/luaforwindowsui
正題:lua
require做用相似於C/C++中的#include,其特性爲:spa
1. 根據搜索目錄加載指定模塊.net
2. 斷定模塊是否已加載,避免重複加載debug
其實現原理:設計
對於require加載的模塊數據是存儲在package.loaded表中,其存儲方式以模塊名爲key,以返回值(模塊若無返回值,默認爲true)爲value進行存儲的。好比運行以下程序:code
-- package.loaded的類型 print(type(package.loaded)) -- table -- 沒有require模塊文件 for i, v in pairs(package.loaded) do print(i,v) end --[[ >>>輸出 string table: 006EDE40 debug table: 006EDF08 package table: 006EDAF8 _G table: 00501C88 io table: 006EDC88 os table: 006EDDC8 table table: 006EDB98 math table: 006EDEB8 ]]
而後咱們任意加載一個Demo.lua,代碼以下:
-- requireDemo.lua -- 內部實現代碼: local requireDemo = {} return true
require("requireDemo") for i, v in pairs(package.loaded) do print(i,v) end --[[ 輸出: string table: 002DDE40 debug table: 002DDF08 package table: 002DDAF8 _G table: 008C1C88 io table: 002DDC88 os table: 002DDDC8 table table: 002DDB98 math table: 002DDEB8 coroutine table: 002DDA58 requireDemo true -- 新加的,已經加入到表中了 ]]
通過如上代碼對比,咱們能夠這樣理解,經過require加載某模塊的時候,首先經過package.loaded來斷定是否已加載。若是已加載,則返回該模塊數據,若是沒有加載,進行搜索加載,若是加載成功,返回該模塊數據,不然報錯。好比:
-- 加載不存在的模塊 require("ErrorModel") --[[ 錯誤堆棧信息: lua: require.lua:21: module 'ErrorModel' not found: no field package.preload['ErrorModel'] no file '.\ErrorModel.lua' no file 'E:\Program Files (x86)\Lua\5.1\lua\ErrorModel.lua' no file 'E:\Program Files (x86)\Lua\5.1\lua\ErrorModel\init.lua' no file 'E:\Program Files (x86)\Lua\5.1\ErrorModel.lua' no file 'E:\Program Files (x86)\Lua\5.1\ErrorModel\init.lua' no file 'E:\Program Files (x86)\Lua\5.1\lua\ErrorModel.luac' no file '.\ErrorModel.dll' no file '.\ErrorModel51.dll' no file 'E:\Program Files (x86)\Lua\5.1\ErrorModel.dll' no file 'E:\Program Files (x86)\Lua\5.1\ErrorModel51.dll' no file 'E:\Program Files (x86)\Lua\5.1\clibs\ErrorModel.dll' no file 'E:\Program Files (x86)\Lua\5.1\clibs\ErrorModel51.dll' no file 'E:\Program Files (x86)\Lua\5.1\loadall.dll' no file 'E:\Program Files (x86)\Lua\5.1\clibs\loadall.dll' stack traceback: [C]: in function 'require' require.lua:21: in main chunk [C]: ? ]]
對比着錯誤的堆棧信息,咱們能夠獲得這樣的信息:根據指定的搜索路徑,查找了Lua和C中的相關文件。咱們來詳細的說明下:
在程序中,lua是經過LUA_PATH進行初始化,C是經過LUA_CPATH進行初始化的
// 在luaconf.h中 // Environment variable names for path overrides and initialization code // 用於初始化和覆蓋環境變量名 #define LUA_PATH "LUA_PATH" #define LUA_CPATH "LUA_CPATH"
若是LUA_PATH,LUA_CPATH沒有相關的變量,會經過LUA_PATH_DEFAULT,LUA_CPATH_DEFAULT來進行默認初始化,代碼以下:
// luaconf.h中 /* ** In Windows, any exclamation mark ('!') in the path is replaced by the ** path of the directory of the executable file of the current process. */ #define LUA_LDIR "!\\lua\\" #define LUA_CDIR "!\\" #define LUA_PATH_DEFAULT \ ".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" #define LUA_CPATH_DEFAULT \ ".\\?.dll;" LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll" #else /* ** Note to distribution maintainers: do NOT patch the following lines! ** Please read ../doc/install.html#distro and pass PREFIX=/usr instead. */ #ifndef LUA_MULTILIB #define LUA_MULTILIB "lib" #endif #ifndef LUA_LMULTILIB #define LUA_LMULTILIB "lib" #endif #define LUA_LROOT "/usr/local" #define LUA_LUADIR "/lua/5.1/" #define LUA_LJDIR "/luajit-2.1.0-beta2/" #ifdef LUA_ROOT #define LUA_JROOT LUA_ROOT #define LUA_RLDIR LUA_ROOT "/share" LUA_LUADIR #define LUA_RCDIR LUA_ROOT "/" LUA_MULTILIB LUA_LUADIR #define LUA_RLPATH ";" LUA_RLDIR "?.lua;" LUA_RLDIR "?/init.lua" #define LUA_RCPATH ";" LUA_RCDIR "?.so" #else #define LUA_JROOT LUA_LROOT #define LUA_RLPATH #define LUA_RCPATH #endif #define LUA_JPATH ";" LUA_JROOT "/share" LUA_LJDIR "?.lua" #define LUA_LLDIR LUA_LROOT "/share" LUA_LUADIR #define LUA_LCDIR LUA_LROOT "/" LUA_LMULTILIB LUA_LUADIR #define LUA_LLPATH ";" LUA_LLDIR "?.lua;" LUA_LLDIR "?/init.lua" #define LUA_LCPATH1 ";" LUA_LCDIR "?.so" #define LUA_LCPATH2 ";" LUA_LCDIR "loadall.so" #define LUA_PATH_DEFAULT "./?.lua" LUA_JPATH LUA_LLPATH LUA_RLPATH #define LUA_CPATH_DEFAULT "./?.so" LUA_LCPATH1 LUA_RCPATH LUA_LCPATH2 #endif
初始化後,會將相關的路徑分別放置到package.path,package.cpath中:
print("package.path路徑相關:") print(package.path) --[[ -- 爲了便於查看,進行了分行 ; .\?.lua; E:\Program Files (x86)\Lua\5.1\lua\?.lua; E:\Program Files (x86)\Lua\5.1\lua\?\init.lua; E:\Program Files (x86)\Lua\5.1\?.lua; E:\Program Files (x86)\Lua\5.1\?\init.lua; E:\Program Files (x86)\Lua\5.1\lua\?.luac ]] print("package.cpath路徑相關:") print(package.cpath) --[[ -- 爲了便於查看,進行了分行 .\?.dll; .\?51.dll; E:\Program Files (x86)\Lua\5.1\?.dll; E:\Program Files (x86)\Lua\5.1\?51.dll; E:\Program Files (x86)\Lua\5.1\clibs\?.dll; E:\Program Files (x86)\Lua\5.1\clibs\?51.dll; E:\Program Files (x86)\Lua\5.1\loadall.dll; E:\Program Files (x86)\Lua\5.1\clibs\loadall.dll ]]
看到如上的輸出,其模塊名的顯示"?",在lua中,其搜索的路徑實質上屬於模板路徑,在搜索模塊時,首先會把模塊名替換模塊路徑下的"?",再進行開始搜索。
從實質上來講,搜索的步驟依次分爲以下部分:
1. 預加載搜索,經過package.preload來進行
2. Lua中搜索,經過package.path獲取搜索路徑,成功後會調用loadFile加載
3. C庫中搜索,經過package.cpath獲取搜索路徑,成功後,會調用loadlib來加載
加載成功後,會存儲到表package.loaded中。
其它:可否從新加載模塊嗎?
答案能夠,咱們須要將已加載模塊的表置空,以下:
package.loaded["*"] = nil -- 置空已加載的模塊數據 required("*") -- 再次加載
關於require的實現,代碼以下:
-- 參考: Lua程序設計(第2版)
function require(name) -- 斷定模塊是否已加載 if not package.loaded[name] then local loader = findloader(name) if loader == nil then error("unable to load module " .. name) end -- 將模塊標記爲已加載 package.loaded[name] = true -- 初始化模塊 local res = loader(name) if res ~= nil then package.loaded[name] = res end end -- 返回模塊數據 return package.loaded[name] end