1. 程式人生 > >lua指令碼學習15 模組與包

lua指令碼學習15 模組與包

Lua 模組與包

模組類似於一個封裝庫,從 Lua 5.1 開始,Lua 加入了標準的模組管理機制,可以把一些公用的程式碼放在一個檔案裡,以 API 介面的形式在其他地方呼叫,有利於程式碼的重用和降低程式碼耦合度。

Lua 的模組是由變數、函式等已知元素組成的 table,因此建立一個模組很簡單,就是建立一個 table,然後把需要匯出的常量、函式放入其中,最後返回這個 table 就行。以下為建立自定義模組 module.lua,檔案程式碼格式如下:

--檔名為module.lua
--定義一個名為module的模組module={}--定義一個常量module.constant 
="這是一個常量"--定義一個函式functionmodule.func1() io.write("這是一個公有函式!\n")endlocalfunction func2()print("這是一個私有函式!")endfunctionmodule.func3() func2()endreturnmodule
檔名為module.lua --定義一個名為module的模組module={}--定義一個常量module.constant ="這是一個常量"--定義一個函式functionmodule.func1() io.write("這是一個公有函式!\n")endlocal
function func2()print("這是一個私有函式!")endfunctionmodule.func3() func2()endreturnmodule

由上可知,模組的結構就是一個 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_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() 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_PATHexport LUA_PATH="~/lua/?.lua;;"export LUA_PATH="~/lua/?.lua;;"

檔案路徑以 ";" 號分隔,最後的 2 個 ";;" 表示新加的路徑後面加上原來的預設路徑。

接著,更新環境變數引數,使之立即生效。

source ~/.profile~/.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.luadengjoe/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.luadengjoe/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") 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()--真正開啟庫 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庫了。

module.lua內容: