lua require

环境:

Lua5.1  LuaForWindows

LuaForWindows的下载地址

http://files.luaforge.net/releases/luaforwindows/luaforwindows

 

正题:

require作用类似于C/C++中的#include,其特性为:

1. 根据搜索目录加载指定模块

2. 判定模块是否已加载,避免重复加载

其实现原理:

  对于require加载的模块数据是存储在package.loaded表中,其存储方式以模块名为key,以返回值(模块若无返回值,默认为true)为value进行存储的。比如运行如下程序:

-- package.loaded的类型
print(type(package.loaded))      -- table
-- 没有require模块文件
for i, v in pairs(package.loaded) do
    print(i,v)
end

--[[
>>>输出
string    table: 006EDE40
debug    table: 006EDF08
package    table: 006EDAF8
_G    table: 00501C88
io    table: 006EDC88
os    table: 006EDDC8
table    table: 006EDB98
math    table: 006EDEB8
]]

然后我们任意加载一个Demo.lua,代码如下:

-- requireDemo.lua
-- 内部实现代码: 
local requireDemo = {}
return true
require("requireDemo")
for i, v in pairs(package.loaded) do
    print(i,v)
end

--[[
输出:
string    table: 002DDE40
debug    table: 002DDF08
package    table: 002DDAF8
_G    table: 008C1C88
io    table: 002DDC88
os    table: 002DDDC8
table    table: 002DDB98
math    table: 002DDEB8
coroutine    table: 002DDA58
requireDemo    true                      -- 新加的,已经加入到表中了        
]]    

经过如上代码对比,我们可以这样理解,通过require加载某模块的时候,首先通过package.loaded来判定是否已加载。如果已加载,则返回该模块数据,如果没有加载,进行搜索加载,如果加载成功,返回该模块数据,否则报错。比如:

-- 加载不存在的模块
require("ErrorModel")

--[[
错误堆栈信息:
lua: require.lua:21: module 'ErrorModel' not found:
    no field package.preload['ErrorModel']
    no file '.\ErrorModel.lua'
    no file 'E:\Program Files (x86)\Lua\5.1\lua\ErrorModel.lua'
    no file 'E:\Program Files (x86)\Lua\5.1\lua\ErrorModel\init.lua'
    no file 'E:\Program Files (x86)\Lua\5.1\ErrorModel.lua'
    no file 'E:\Program Files (x86)\Lua\5.1\ErrorModel\init.lua'
    no file 'E:\Program Files (x86)\Lua\5.1\lua\ErrorModel.luac'
    no file '.\ErrorModel.dll'
    no file '.\ErrorModel51.dll'
    no file 'E:\Program Files (x86)\Lua\5.1\ErrorModel.dll'
    no file 'E:\Program Files (x86)\Lua\5.1\ErrorModel51.dll'
    no file 'E:\Program Files (x86)\Lua\5.1\clibs\ErrorModel.dll'
    no file 'E:\Program Files (x86)\Lua\5.1\clibs\ErrorModel51.dll'
    no file 'E:\Program Files (x86)\Lua\5.1\loadall.dll'
    no file 'E:\Program Files (x86)\Lua\5.1\clibs\loadall.dll'
stack traceback:
    [C]: in function 'require'
    require.lua:21: in main chunk
    [C]: ?
]]

对比着错误的堆栈信息,我们可以得到这样的信息:根据指定的搜索路径,查找了Lua和C中的相关文件。我们来详细的说明下:

在程序中,lua是通过LUA_PATH进行初始化,C是通过LUA_CPATH进行初始化的

// 在luaconf.h中
// Environment variable names for path overrides and initialization code
// 用于初始化和覆盖环境变量名
#define LUA_PATH    "LUA_PATH"
#define LUA_CPATH    "LUA_CPATH"

如果LUA_PATH,LUA_CPATH没有相关的变量,会通过LUA_PATH_DEFAULT,LUA_CPATH_DEFAULT来进行默认初始化,代码如下:

// luaconf.h中
/*
** In Windows, any exclamation mark ('!') in the path is replaced by the
** path of the directory of the executable file of the current process.
*/
#define LUA_LDIR    "!\\lua\\"
#define LUA_CDIR    "!\\"
#define LUA_PATH_DEFAULT \
  ".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;"
#define LUA_CPATH_DEFAULT \
  ".\\?.dll;" LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll"
#else
/*
** Note to distribution maintainers: do NOT patch the following lines!
** Please read ../doc/install.html#distro and pass PREFIX=/usr instead.
*/
#ifndef LUA_MULTILIB
#define LUA_MULTILIB    "lib"
#endif
#ifndef LUA_LMULTILIB
#define LUA_LMULTILIB    "lib"
#endif
#define LUA_LROOT    "/usr/local"
#define LUA_LUADIR    "/lua/5.1/"
#define LUA_LJDIR    "/luajit-2.1.0-beta2/"

#ifdef LUA_ROOT
#define LUA_JROOT    LUA_ROOT
#define LUA_RLDIR    LUA_ROOT "/share" LUA_LUADIR
#define LUA_RCDIR    LUA_ROOT "/" LUA_MULTILIB LUA_LUADIR
#define LUA_RLPATH    ";" LUA_RLDIR "?.lua;" LUA_RLDIR "?/init.lua"
#define LUA_RCPATH    ";" LUA_RCDIR "?.so"
#else
#define LUA_JROOT    LUA_LROOT
#define LUA_RLPATH
#define LUA_RCPATH
#endif

#define LUA_JPATH    ";" LUA_JROOT "/share" LUA_LJDIR "?.lua"
#define LUA_LLDIR    LUA_LROOT "/share" LUA_LUADIR
#define LUA_LCDIR    LUA_LROOT "/" LUA_LMULTILIB LUA_LUADIR
#define LUA_LLPATH    ";" LUA_LLDIR "?.lua;" LUA_LLDIR "?/init.lua"
#define LUA_LCPATH1    ";" LUA_LCDIR "?.so"
#define LUA_LCPATH2    ";" LUA_LCDIR "loadall.so"

#define LUA_PATH_DEFAULT    "./?.lua" LUA_JPATH LUA_LLPATH LUA_RLPATH
#define LUA_CPATH_DEFAULT    "./?.so" LUA_LCPATH1 LUA_RCPATH LUA_LCPATH2
#endif

初始化后,会将相关的路径分别放置到package.path,package.cpath中:

print("package.path路径相关:")
print(package.path)
--[[
-- 为了便于查看,进行了分行
;
.\?.lua;
E:\Program Files (x86)\Lua\5.1\lua\?.lua;
E:\Program Files (x86)\Lua\5.1\lua\?\init.lua;
E:\Program Files (x86)\Lua\5.1\?.lua;
E:\Program Files (x86)\Lua\5.1\?\init.lua;
E:\Program Files (x86)\Lua\5.1\lua\?.luac
]]

print("package.cpath路径相关:")
print(package.cpath)
--[[
-- 为了便于查看,进行了分行
.\?.dll;
.\?51.dll;
E:\Program Files (x86)\Lua\5.1\?.dll;
E:\Program Files (x86)\Lua\5.1\?51.dll;
E:\Program Files (x86)\Lua\5.1\clibs\?.dll;
E:\Program Files (x86)\Lua\5.1\clibs\?51.dll;
E:\Program Files (x86)\Lua\5.1\loadall.dll;
E:\Program Files (x86)\Lua\5.1\clibs\loadall.dll
]]

看到如上的输出,其模块名的显示"?",在lua中,其搜索的路径实质上属于模板路径,在搜索模块时,首先会把模块名替换模块路径下的"?",再进行开始搜索。

从实质上来说,搜索的步骤依次分为如下部分:

1. 预加载搜索,通过package.preload来进行

2. Lua中搜索,通过package.path获取搜索路径,成功后会调用loadFile加载

3. C库中搜索,通过package.cpath获取搜索路径,成功后,会调用loadlib来加载

加载成功后,会存储到表package.loaded中。

 

其它:能否重新加载模块吗?

答案可以,我们需要将已加载模块的表置空,如下:

package.loaded["*"] = nil      -- 置空已加载的模块数据
required("*")             -- 再次加载

 关于require的实现,代码如下:

-- 参考: Lua程序设计(第2版)
function
require(name) -- 判定模块是否已加载 if not package.loaded[name] then local loader = findloader(name) if loader == nil then error("unable to load module " .. name) end -- 将模块标记为已加载 package.loaded[name] = true -- 初始化模块 local res = loader(name) if res ~= nil then package.loaded[name] = res end end -- 返回模块数据 return package.loaded[name] end

 

转载于:https://www.cnblogs.com/SkyflyBird/p/7851752.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值