1. 程式人生 > >require載入模組

require載入模組

Lua 的模組是由變數、函式等已知元素組成的 table,因此建立一個模組很簡單,就是建立一個 table,然後把需要匯出的常量、函式放入其中,最後返回這個 table 就行,Lua提供了一個名為require的函式用來載入模組。要載入一個模組,只需要簡單地呼叫就可以了,執行以下語句 require 會返回一個由模組常量或函式組成的 table,並且還會定義一個包含該 table 的全域性變數。

require("module")--nodule.lua檔案

這裡先將載入過程中涉及到的package想關資訊說明下

package.path

這個路徑被 require 在 Lua 載入器中做搜尋時用到。
在啟動時,Lua 用環境變數 LUA_PATH_5_3 或環境變數 LUA_PATH 來初始化這個變數。 或採用 luaconf.h 中的預設路徑。 環境變數中出現的所有 ";;"都會被替換成預設路徑

package.cpath

 這個路徑被 require 在 C 載入器中做搜尋時用到。
Lua 用和初始化 Lua 路徑 package.path 相同的方式初始化 C 路徑 package.cpath 。 它會使用環境變數 LUA_CPATH_5_3 或 環境變數 LUA_CPATH 初始化。要麼就採用 luaconf.h 中定義的預設路徑。

package.loaded

 用於 require 控制哪些模組已經被載入的表。 當你請求一個 modname 模組,且 package.loaded[modname] 不為假時, require 簡單返回儲存在內的值。這個變數僅僅是對真正那張表的引用; 改變這個值並不會改變 require 使用的表。

package.loadlib (libname, funcname)

 讓宿主程式動態連結 C 庫 libname 。
當 funcname 為 "*", 它僅僅連線該庫,讓庫中的符號都匯出給其它動態連結庫使用。 否則,它查詢庫中的函式 funcname ,
以 C 函式的形式返回這個函式。 因此,funcname 必須遵循原型 lua_CFunction (參見 lua_CFunction)。
這是一個低階函式。 它完全繞過了包模組系統。 和 require 不同, 它不會做任何路徑查詢,也不會自動加副檔名。 libname 必須是一個 C庫需要的完整的檔名,如果有必要,需要提供路徑和副檔名。 funcname 必須是 C 庫需要的準確名字 (這取決於使用的 C 編譯器和連結器)。這個函式在標準 C 中不支援。 因此,它只在部分平臺有效 ( Windows ,Linux ,Mac OS X, Solaris, BSD, 加上支援 dlfcn 標準的 Unix 系統)。

package.preload

 儲存有一些特殊模組的載入器 (參見 require)。
這個變數僅僅是對真正那張表的引用; 改變這個值並不會改變 require 使用的表。

package.searchers

 用於 require 控制如何載入模組的表。
這張表內的每一項都是一個 查詢器函式。 當查詢一個模組時, require 按次序呼叫這些查詢器, 並傳入模組名(require 的引數)作為唯一的一個引數。 此函式可以返回另一個函式(模組的 載入器)加上另一個將傳遞給這個載入器的引數。 或是返回一個描述為何沒有找到這個模組的字串 (或是返回 nil 什麼也不想說)。
Lua 用四個查詢器函式初始化這張表。
第一個查詢器就是簡單的在 package.preload 表中查詢載入器。
第二個查詢器用於查詢 Lua 庫的載入庫。 它使用儲存在 package.path 中的路徑來做查詢工作。 查詢過程和函式 package.searchpath 描述的一致。
第三個查詢器用於查詢 C 庫的載入庫。 它使用儲存在 package.cpath 中的路徑來做查詢工作。 同樣, 查詢過程和函式 package.searchpath 描述的一致。 例如,如果 C 路徑是這樣一個字串
     "./?.so;./?.dll;/usr/local/?/init.so"
查詢器查詢模組 foo 會依次嘗試開啟檔案 ./foo.so,./foo.dll, 以及 /usr/local/foo/init.so。 一旦它找到一個 C 庫, 查詢器首先使用動態連結機制連線該庫。 然後嘗試在該庫中找到可以用作載入器的 C 函式。 這個 C 函式的名字是 "luaopen_" 緊接模組名的字串, 其中字串中所有的下劃線都會被替換成點。 此外,如果模組名中有橫線, 橫線後面的部分(包括橫線)都被去掉。 例如,如果模組名為 a.b.c-v2.1, 函式名就是 luaopen_a_b_c。
第四個搜尋器是 一體化載入器。 它從 C 路徑中查詢指定模組的根名字。 例如,當請求 a.b.c 時, 它將查詢 a 這個 C 庫。 如果找得到,它會在裡面找子模組的載入函式。 在我們的例子中,就是找 luaopen_a_b_c。 利用這個機制,可以把若干 C 子模組打包進單個庫。 每個子模組都可以有原本的載入函式名。
除了第一個(預載入)搜尋器外,每個搜尋器都會返回 它找到的模組的檔名。 這和 package.searchpath 的返回值一樣。 第一個搜尋器沒有返回值。

package.searchpath (name, path [, sep [, rep]])

 在指定 path 中搜索指定的 name 。
路徑是一個包含有一系列以分號分割的 模板 構成的字串。 對於每個模板,都會用 name 替換其中的每個問號(如果有的話)。 且將其中的 sep (預設是點)替換為 rep (預設是系統的目錄分割符)。 然後嘗試開啟這個檔名。
例如,如果路徑是字串
"./?.lua;./?.lc;/usr/local/?/init.lua"
搜尋 foo.a 這個名字將 依次嘗試開啟檔案 ./foo/a.lua , ./foo/a.lc ,以及 /usr/local/foo/a/init.lua。
返回第一個可以用讀模式開啟(並馬上關閉該檔案)的檔案的名字。 如果不存在這樣的檔案,返回 nil 加上錯誤訊息。 (這條錯誤訊息列出了所有嘗試開啟的檔名。)

 

一般我們載入一個模組的步驟如下:

1、載入一個模組。 require函式首先查詢package.loaded表, 檢測modname是否被載入過。 如果被載入過,require返回 package.loaded[modname]中儲存的值
    否則,它試著為模組尋找載入器 。
2、lua的載入器儲存在package的searchers表中,共有四個載入器,require也按照順序來執行載入器。找到了就成功返回,沒有就繼續查詢。首選查詢預載入器,執行package.preload[modname],如果這裡有值,這個值(必須是一個函式)就是那個載入器, 否則 require 使用 Lua 載入器去查詢package.path 的路徑。 如果查詢失敗,接著使用C載入器去查詢package.cpath的路徑。如果都失敗了,再嘗試一體化載入器(參見 package.searchers)
 每次找到一個載入器,require 都用兩個引數呼叫載入器: modname 和一個在獲取載入器過程中得到的引數。(如果通過查詢檔案得到的載入器,這個額外引數是檔名。) 如果載入器返回非空值, require 將這個值賦給 package.loaded[modname]。 如果載入器沒能返回一個非空值用於賦給 package.loaded[modname], require 會在那裡設入 true 。 無論是什麼情況,require 都會返回 package.loaded[modname] 的最終值。
如果在載入或執行模組時有錯誤, 或是無法為模組找到載入器, require 都會丟擲錯誤。

 

參考:http://www.runoob.com/manual/lua53doc/manual.html#pdf-string.format