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