咱们以前实现了本身版本的require,首先认定为lua模块尝试加载,若是加载不成功则认定为C模块继续进行加载。实际上,在Lua内部,是经过searchers来区分不一样的加载方式。Lua一共有4种searchers,用来加载lua模块的和加载C模块的分别是第2个和第3个。第1个searcher叫作preload,它使用package.preload
这个内部table,根据加载模块的名称,去找到对应的加载函数来加载模块,例如:微信
-- mypackage.lua print("hello world mypackage")
咱们执行:函数
print(next(package.preload)) package.preload["mypackage"] = function(module) print("module ", module) end require("mypackage") require("mypackage")
输出以下:ui
能够看到,默认状况下package.preload
是一个空的table,咱们为mypackge模块指定一个自定义的加载函数后,调用require就会调用咱们自定义的函数了。一样地,若是这个函数没有返回值,lua会为之添加一个true
的返回值,标记该模块已经加载过,无需重复加载。lua
咱们还能够经过如下代码,来确认lua的第1个searcher为preload:设计
package.preload["mypackage"] = function(module) print("module ", module) end package.searchers[1] = function(module) print("searchers 1 ", module) return function(module) print("my loader") end end package.searchers[2] = nil require("mypackage")
首先,package.searchers
中的每个searcher都要返回一个loader函数表明加载成功,不然即为加载失败,lua会依次在这个list中继续调用下一个searcher,直到返回一个loader函数。所以,这里咱们显式地把package.searchers
的长度缩减为1,避免lua调用其余searcher影响咱们对结果的判断。输出以下:code
能够发现,package.preload
中的函数并无被调用,调用的其实是咱们重写过的package.searchers[1]
。这就说明了第1个searcher即为preload。blog
一样地,咱们能够用相似的手段,去发现第2个searcher是返回加载lua模块的loader,第3个searcher是返回加载C模块的loader:游戏
package.searchers[2] = function(module) return function(module) print("my loader") end end require("mypackage")
package.searchers[3] = function(module) return function(module) print("my loader") end end -- WinFeature.dll require("WinFeature")
有时,咱们须要在一个模块中定义子模块,lua自己也支持了加载子模块的机制。模块的层级用.
号来区分。例如咱们尝试加载一个子模块:ip
require("a.b")
输出结果以下:游戏开发
能够看到,若是是lua模块,那么子模块至关于父模块的子目录下。若是是C++模块,那么状况有些特殊,子模块除了可能存在父模块的子目录下,也有可能就和父模块在同一个dll中。负责搜索和父模块在同一个dll的searcher就是第4个searcher。相似咱们也能够作出以下验证:
package.searchers[4] = nil require("a.b")
执行,此时输出结果以下:
对比一下,便可获得答案。第4个searcher就是专门用来搜索包含多个子模块的dll的。该dll须要暴露给lua的导出函数名为luaopen_a_b
。
而后,让咱们回到模块自己中来。通常,咱们是这样编写一个模块的:
-- mypackage.lua local M = {} local privateField = 1 M.publicField = 1 local privateFunc = function() print("i am a private function") end M.publicFunc = function() print("i am a public function") end return M
local定义的都只在本模块中可见,能够认为是私有的成员和函数。咱们执行:
m = require("mypackage") print(m.privateField) print(m.publicField) m:privateFunc() m:publicFunc()
不过,若是咱们在某个模块中定义了全局变量,在require以后,也会引入到调用require的环境中,这每每是咱们不想要的:
-- mypackage.lua local M = {} globalField = 1 globalFunc = function() print("i am a global function") end return M
咱们以前在编写本身的require时提过,能够在loadFile
函数传入一个环境参数,来禁止模块定义全局变量,这里咱们能够灵活修改一下,让模块中的全局变量变成只属于该模块自身的一个变量。
最后,让咱们根据lua的设计,从新实现一下require:
local env = {} local searchers = {} searchers[1] = function(module) local loader = package.preload[module] return loader end searchers[2] = function(module) module = string.gsub(module, '%.', '/') for pattern in string.gmatch(package.path, '[^;]+%?[^;]+') do local path = string.gsub(pattern, '%?', module) setmetatable(env, {__index = _G, __newindex = function(t, k, v) rawset(t, k, v) end}) local loader = loadfile(path, nil, env) if loader then return loader end end end searchers[3] = function(module) module = string.gsub(module, '%.', '/') for pattern in string.gmatch(package.cpath, '[^;]+%?[^;]+') do local path = string.gsub(pattern, '%?', module) local loader = package.loadlib(path, "luaopen_" .. module) if loader then return loader end end end searchers[4] = function(module) local dotIndex = string.find(module, '%.') if not dotIndex then return end local lib = string.sub(module, 1, dotIndex - 1) module = string.gsub(module, '%.', '_') for pattern in string.gmatch(package.cpath, '[^;]+%?[^;]+') do local path = string.gsub(pattern, '%?', lib) local loader = package.loadlib(path, "luaopen_" .. module) if loader then return loader end end end function require_ex(module) if package.loaded[module] then return package.loaded[module] end env = {} for _, searcher in ipairs(searchers) do local loader = searcher(module) if loader and type(loader) == "function" then local ret = loader(module) if type(ret) ~= "table" then package.loaded[module] = env else for k, v in pairs(env) do if ret[k] then print("set multiple value") else ret[k] = v end end package.loaded[module] = ret end return package.loaded[module] end end end
若是你以为个人文章有帮助,欢迎关注个人微信公众号(大龄社畜的游戏开发之路)-