【lua 5.1 的 module】c++
lua 從 5.1 開始終於官方提供統一的 module 實現標準了,這是個值得慶幸的事。今天讀了下相關的源碼和文檔,把這套機制搞清楚了,仍是很巧妙的。從簡潔這個角度看,要比 python 強 :)函數
有一點容易被忽略掉(個人同事在用的時候就忽略掉了),module 指令運行完後,整個環境被壓棧,因此前面全局的東西再看不見了。好比定義了一個 test 模塊,使用ui
module("test")lua
後,下面再也不看的見前面的全局環境。若是在這個模塊裏想調用 print 輸出調試信息怎麼辦呢?一個簡單的方法是spa
local print=print
module("test")設計
這樣 print 是一個 local 變量,下面也是可見的。或者能夠用調試
local _G=_G
module("test")blog
那麼 _G.print 也是能夠用的。遞歸
固然還有一種巧妙的方式,lua 5.1 提供了一個 package.seeall 能夠做爲 module 的option 傳入
module("test",package.seeall)
這樣就 OK 了。至於它們是如何工做的,仍是本身讀源碼會理解的清楚一些。
在讀源碼時能夠發現不少 lua 的技巧,還有一些 undocumented 的東西,好比 newproxy :) 它是一個 unsupported 且 undocumented 的東西,可是它但願實現的倒是個巧妙的玩意。
功能:創建一個模塊。
當package.loaded[name]中存在時,當中的表做爲module;
當在全局表中存在name指定的表時,此表做爲module;
當之前兩種狀況都不存表name時,將新建一個表,並使其做爲全局名name的值,並package.loaded[name],並且設 t._NAME爲name,t._M爲module,t._PACKAGE爲包的全名(模塊名-組件a.b.c);最後把此module設t做爲當前函數 的新環境表和package.loaded[name]的新值(也就是說,舊的環境表將不能訪問,除了加上package.seeall參數外),以被 require使用
module(name)後的可選參數爲接收module名的函數,如package.seeall
【lua 5.1 的 require】
require (modname)
功能:加載指定的模塊。
此函數先檢測package.loaded表中是否存在modname,存在則直接返回當中的值,沒有則經過郰定義的加載器加載modname。
查找加載器順序:
(1)檢測package.preload表是否存在modname,有則加載
(2)經過Lua Loader加載,經過查找存放於package.path的路徑加載,有則加載
(3)經過C Loader加載,經過查找存放於package.cpath的路徑加載,有則加載
(4)經過all-in-one Loader加載:
經過查找modname.dll並查找當中的luaopen_
其中XXXX爲載塊名-後的字符用_替換.後的字符:如:a.v1-b.c 當函數名爲luaopen_b_c
當require查找的不是一個Lua庫或C庫,它就會調用all-in-one loader,此加載器是用C路徑做爲載塊的目錄,
當查找到合適的加載器時,require就會加載其中的模塊,當加載器有返回值,將會存放於package.loaded[modname]表。最後返回package.loaded[modname]表
當加載失敗時,require將觸發錯誤
三、package.cpath
功能:用於require C loader的搜索路徑
能夠經過修改LUA_CPATH變量(luaconf.h)修改此值
四、package.loaded
功能:一個用於讓require知道哪些模塊已加載的記錄表,若是package.loaded已經有require要的值,則直接返回此值
五、package.loadlib (libname, funcname)
功能:經過動態鏈接C函數庫方式加載Lua擴展庫
libname爲庫文件名,funcname爲入口函數(此函數必須爲純C接口函數 c++則需用 extern "C" {} 進行限制)
六、package.path
功能:用於require Lua loader的搜索路徑
能夠經過修改LUA_PATH變量(luaconf.h)修改此值
七、package.preload
功能:一個用於保存特殊模塊加載器的表
八、package.seeall(module)
功能:爲module設置一個元表,此元表的__index字段的值爲全局環境_G。因此module能夠訪問全局環境
注:以此函數做爲module()的一個選項(詳細見module()
注意:require只認文件名,不認路徑名。要加入路徑名信息的話,就要寫成父模塊子模塊的形式。好比說,我有兩個文件夾 testa, testb,在每一個文件夾裏面都有一個run.lua文件。我先在lua裏面chdir進到testa裏面去require了一下run.lua,而後再 chdir出來,再chdir進testb,而後,再執行require "run"。這個時候,Lua是默認不會把第二個文件夾中的 run.lua給加載進來的,由於Lua會對加載進來的全部模塊有一個按名字的管理系統,記錄在 package.loaded 裏面。若是要強制加載新的run.lua文件,就得把老的模塊記錄給清掉,即便用 package.loaded["run"] = nil,而後再加載。實際上,Lua這樣設計的目的是爲了防止對模塊的重複加載──這樣能夠解決開發過程當中常常遇到的重複包含的問題,還可解決遞歸包含的問題。咱們一般在C語言的頭文件中寫上#ifdef #define #endif之類的結構,也是爲了實現這種效果。任何設計都不是萬能的,須要開發者根據實際狀況靈活變通。