可以打印package.path查看用那些搜索路径也可以 添加自己的路径
following code
为了方便代码管理,通常会把lua代码分成不同的模块,然后在通过require函数把它们加载进来。现在看看lua的require的处理流程。函数原型:require(modname)---modname ---->加载的模块名称
首先Lua提供高级的require函数来加载运行库。粗略的说require和dofile完成同样的功能但有两点不同:
1、require会搜索目录加载文件
2、require会判断是否文件已经加载避免重复加载同一文件。
由于上述特征,require在Lua中是加载库的更好的函数。
require函数实现了不同lua文件的加载,类似于C++中的include,java中的import。
require使用的路径和普通的路径还是有些区别,我们一般见到的路径都是一个目录列表。require的路径是一个模式列表,每一个模式指明一种由虚文件名(require的参数modname)转成实文件名的方法。更明确地说,每一个模式是一个包含可选的问号(?)的文件名。匹配的时候Lua会首先将问号用虚文件名替换,然后看是否有这样的文件存在。如果不存在继续用同样的方法用第二个模式匹配。例如,路径如下:
??.luac:\windows\?/usr/local/lua/?/?.lua调用require("add")时会试着打开以下这些文件:
add
add.lua
c:\windows\add
/usr/local/lua/add/add.lua
为了确定路径,Lua首先检查全局变量LUA_PATH是否为一个字符串,如果是则认为这个串就是路径;否则require检查环境变量LUA_PATH的值,如果两个都失败require使用固定的路径(典型的"??.lua").
require函数的实现原理如下:
--require 函数的实现
function require(name)
if not package.loaded[name] then
local loader = findloader(name) //这一步演示在代码中以抽象函数findloader来表示
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
require(在lua中它是ll_require函数)函数会在路径中搜索输入的文件路径,大致流程如下:
1、package.loaded
一个用于控制哪些模块已经加载的表,该表由require使用。当require一个模块名为modname的模块且package.loaded[modname]不为false时,require仅返回package.loaded[modname]存储的值.
2、 package.preload
为特定模块存储加载器的一个表。查找modname, 如果preload存在,那么就把它作为loader,调用loader(L)
3、package.path
查找lua库modname,这个库是通过module函数定义的,对于顶层的lua库,文件名和库名是一 样的而且不需要调用显式地在lua文件中调用module函数(在ll_require函数中可以看到处理方式),也就是说lua会根据lua文件直接完 成一个loader的初始化过程。
4、package.cpath(实现lua调用C函数)
查找c库,这个库是符合lua的一些规范的(export具有一定特征的函数接口),lua先已动态的方式加载该c库(.so),然后在库中查找并调用相应名字的接口,例如:luaopen_hello_world
5、已第一个"."为分割,将模块名划分为:(main, sub)的形式,根据package.cpath查找main,如果存在,就加载该库并查询相应的接口:luaopen_main_sub,例如:先查找 hello库,并查询luaopen_hello_world接口
6、得到loder后,用modname作为唯一的参数调用该loader函数。当然参数是通过lua的栈传递的,所以loader的原型必须符合lua的规范:int LUA_FUNC(lua_State *L)
ll_require会将这个loader的返回值符给package.loaded[modelname],如果loader不返回值同时 package.loaded[modelname]不存在时, ll_require就会把package.loaded[modelname]设为true。最后ll_reuqire把package.loaded [modelname]返回给调用者。
require的另一个功能是避免重复加载同一个文件两次。Lua保留一张所有已经加载的文件的列表(使用table保存)。如果一个加载的文件在表中存在require简单的返回;表中保留加载的文件的虚名,而不是实文件名。所以如果你使用不同的虚文件名require同一个文件两次,将会加载两次该文件。比如require "foo"和require "foo.lua",路径为"??.lua"将会加载foo.lua两次。我们也可以通过全局变量_LOADED访问文件名列表,这样我们就可以判断文件是否被加载过;同样我们也可以使用一点小技巧让require加载一个文件两次。比如,require "foo"之后_LOADED["foo"]将不为nil,我们可以将其赋值为nil,require "foo.lua"将会再次加载该文件。
Lua提供高级的require函数来加载运行库,lua中的require函数功能主要有:
1、require函数会搜索目录加载文件
2、require会判断是否文件已经加载避免重复加载同一文件。
由于上述特征,require的路径是一个模式列表,每一个模式指明一种由虚文件名(require的参数)转成实文件名的方法。更明确地说,每一个模式是一个包含可选的问号的文件名。匹配的时候Lua会首先将问号用虚文件名替换,然后看是否有这样的文件存在。如果不存在继续用同样的方法用第二个模式匹配。
为了确定路径,Lua首先检查全局变量LUA_PATH是否为一个字符串,如果是则认为这个串就是路径;否则require检查环境变量LUA_PATH的值,如果两个都失败require使用固定的路径(典型的??.lua)
require的另一个功能是避免重复加载同一个文件两次。Lua保留一张所有已经加载的文件的列表(使用table保存)。如果一个加载的文件在表中存在require简单的返回;表中保留加载的文件的虚名,而不是实文件名。所以如果你使用不同的虚文件名require同一个文件两次,将会加载两次该文件。比如require foo和require foo.lua,路径为??.lua将会加载foo.lua两次。我们也可以通过全局变量_LOADED访问文件名列表,这样我们就可以判断文件是否被加载过;同样我们也可以使用一点小技巧让require加载一个文件两次。比如,require foo之后_LOADED[foo]将不为nil,我们可以将其赋值为nil,require foo.lua将会再次加载该文件。一个路径中的模式也可以不包含问号而只是一个固定的路径。
这种情况下,require没有匹配的时候就会使用这个固定的文件(当然这个固定的路径必须放在模式列表的最后才有意义)。在require运行一个chunk以前,它定义了一个全局变量_REQUIREDNAME用来保存被required的虚文件的文件名。我们可以通过使用这个技巧扩展require的功能。
举个极端的例子,我们可以把路径设为/usr/local/lua/newrequire.lua,这样以后每次调用require都会运行newrequire.lua,这种情况下可以通过使用_REQUIREDNAME的值去实际加载required的文件。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)