模塊類(lèi)似于一個(gè)封裝庫(kù),從 Lua 5.1 開(kāi)始,Lua 加入了標(biāo)準(zhǔn)的模塊管理機(jī)制,可以把一些公用的代碼放在一個(gè)文件里,以 API 接口的形式在其他地方調(diào)用,有利于代碼的重用和降低代碼耦合度。
Lua 的模塊是由變量、函數(shù)等已知元素組成的 table,因此創(chuàng)建一個(gè)模塊很簡(jiǎn)單,就是創(chuàng)建一個(gè) table,然后把需要導(dǎo)出的常量、函數(shù)放入其中,最后返回這個(gè) table 就行。以下為創(chuàng)建自定義模塊 module.lua,文件代碼格式如下:
-- 文件名為 module.lua -- 定義一個(gè)名為 module 的模塊 module = {} -- 定義一個(gè)常量 module.constant = "這是一個(gè)常量" -- 定義一個(gè)函數(shù) function module.func1() io.write("這是一個(gè)公有函數(shù)!\n") end local function func2() print("這是一個(gè)私有函數(shù)!") end function module.func3() func2() end return module
由上可知,模塊的結(jié)構(gòu)就是一個(gè) table 的結(jié)構(gòu),因此可以像操作調(diào)用 table 里的元素那樣來(lái)操作調(diào)用模塊里的常量或函數(shù)。
上面的 func2 聲明為程序塊的局部變量,即表示一個(gè)私有函數(shù),因此是不能從外部訪(fǎng)問(wèn)模塊里的這個(gè)私有函數(shù),必須通過(guò)模塊里的公有函數(shù)來(lái)調(diào)用.
Lua提供了一個(gè)名為require的函數(shù)用來(lái)加載模塊。要加載一個(gè)模塊,只需要簡(jiǎn)單地調(diào)用就可以了。例如:
require("<模塊名>")
或者
require "<模塊名>"
執(zhí)行 require 后會(huì)返回一個(gè)由模塊常量或函數(shù)組成的 table,并且還會(huì)定義一個(gè)包含該 table 的全局變量。
-- test_module.php 文件 -- module 模塊為上文提到到 module.lua require("module") print(module.constant) module.func3()
以上代碼執(zhí)行結(jié)果為:
這是一個(gè)常量 這是一個(gè)私有函數(shù)!
或者給加載的模塊定義一個(gè)別名變量,方便調(diào)用:
-- test_module2.php 文件 -- module 模塊為上文提到到 module.lua -- 別名變量 m local m = require("module") print(m.constant) m.func3()
以上代碼執(zhí)行結(jié)果為:
這是一個(gè)常量 這是一個(gè)私有函數(shù)!
對(duì)于自定義的模塊,模塊文件不是放在哪個(gè)文件目錄都行,函數(shù) require 有它自己的文件路徑加載策略,它會(huì)嘗試從 Lua 文件或 C 程序庫(kù)中加載模塊。
require 用于搜索 Lua 文件的路徑是存放在全局變量 package.path 中,當(dāng) Lua 啟動(dòng)后,會(huì)以環(huán)境變量 LUA_PATH 的值來(lái)初始這個(gè)環(huán)境變量。如果沒(méi)有找到該環(huán)境變量,則使用一個(gè)編譯時(shí)定義的默認(rèn)路徑來(lái)初始化。
當(dāng)然,如果沒(méi)有 LUA_PATH 這個(gè)環(huán)境變量,也可以自定義設(shè)置,在當(dāng)前用戶(hù)根目錄下打開(kāi) .profile 文件(沒(méi)有則創(chuàng)建,打開(kāi) .bashrc 文件也可以),例如把 "~/lua/" 路徑加入 LUA_PATH 環(huán)境變量里:
#LUA_PATH export LUA_PATH="~/lua/?.lua;;"
文件路徑以 ";" 號(hào)分隔,最后的 2 個(gè) ";;" 表示新加的路徑后面加上原來(lái)的默認(rèn)路徑。
接著,更新環(huán)境變量參數(shù),使之立即生效。
source ~/.profile
這時(shí)假設(shè) package.path 的值是:
/Users/dengjoe/lua/?.lua;./?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/usr/local/lib/lua/5.1/?.lua;/usr/local/lib/lua/5.1/?/init.lua
那么調(diào)用 require("module") 時(shí)就會(huì)嘗試打開(kāi)以下文件目錄去搜索目標(biāo)。
/Users/dengjoe/lua/module.lua; ./module.lua /usr/local/share/lua/5.1/module.lua /usr/local/share/lua/5.1/module/init.lua /usr/local/lib/lua/5.1/module.lua /usr/local/lib/lua/5.1/module/init.lua
如果找過(guò)目標(biāo)文件,則會(huì)調(diào)用 package.loadfile 來(lái)加載模塊。否則,就會(huì)去找 C 程序庫(kù)。
搜索的文件路徑是從全局變量 package.cpath 獲取,而這個(gè)變量則是通過(guò)環(huán)境變量 LUA_CPATH 來(lái)初始。
搜索的策略跟上面的一樣,只不過(guò)現(xiàn)在換成搜索的是 so 或 dll 類(lèi)型的文件。如果找得到,那么 require 就會(huì)通過(guò) package.loadlib 來(lái)加載它。
Lua和C是很容易結(jié)合的,使用C為L(zhǎng)ua寫(xiě)包。
與Lua中寫(xiě)包不同,C包在使用以前必須首先加載并連接,在大多數(shù)系統(tǒng)中最容易的實(shí)現(xiàn)方式是通過(guò)動(dòng)態(tài)連接庫(kù)機(jī)制。
Lua在一個(gè)叫l(wèi)oadlib的函數(shù)內(nèi)提供了所有的動(dòng)態(tài)連接的功能。這個(gè)函數(shù)有兩個(gè)參數(shù):庫(kù)的絕對(duì)路徑和初始化函數(shù)。所以典型的調(diào)用的例子如下:
local path = "/usr/local/lua/lib/libluasocket.so" local f = loadlib(path, "luaopen_socket")
loadlib函數(shù)加載指定的庫(kù)并且連接到Lua,然而它并不打開(kāi)庫(kù)(也就是說(shuō)沒(méi)有調(diào)用初始化函數(shù)),反之他返回初始化函數(shù)作為L(zhǎng)ua的一個(gè)函數(shù),這樣我們就可以直接在Lua中調(diào)用他。
如果加載動(dòng)態(tài)庫(kù)或者查找初始化函數(shù)時(shí)出錯(cuò),loadlib將返回nil和錯(cuò)誤信息。我們可以修改前面一段代碼,使其檢測(cè)錯(cuò)誤然后調(diào)用初始化函數(shù):
local path = "/usr/local/lua/lib/libluasocket.so" -- 或者 path = "C:\\windows\\luasocket.dll",這是 Window 平臺(tái)下 local f = assert(loadlib(path, "luaopen_socket")) f() -- 真正打開(kāi)庫(kù)
一般情況下我們期望二進(jìn)制的發(fā)布庫(kù)包含一個(gè)與前面代碼段相似的stub文件,安裝二進(jìn)制庫(kù)的時(shí)候可以隨便放在某個(gè)目錄,只需要修改stub文件對(duì)應(yīng)二進(jìn)制庫(kù)的實(shí)際路徑即可。
將stub文件所在的目錄加入到LUA_PATH,這樣設(shè)定后就可以使用require函數(shù)加載C庫(kù)了。
更多建議: