从字符串动态创建 Lua 模块

我处于以下情况中(请不要质疑我的动机):

我有一个 Lua 脚本 foo.lua,它需要一些模块 bar,即:

-- foo.lua
require 'bar'

-- 更多代码

然而却没有实现此模块的 bar.lua 文件。相反,我想要做的是从另一个脚本 main.lua 中执行 foo.lua,该脚本还应该 _从字符串动态创建模块 bar_(字符串来自何处并不重要),即:

-- main.lua

-- 以某种方式创建模块 `bar`,使得 `foo.lua` 中的 `require bar` 能够成功

dofile('foo.lua')

这是可能的吗?

点赞
用户369792
用户369792

我认为最好的方法是使用 package.preload,它可以使你为给定的模块名称分配一个模块解析器函数:

local myBar = { message = "Hello from BAR!" }

package.preload['bar'] = function(...)
    return myBar
end

你也可以直接在 package.loaded 中设置模块,这样可以覆盖已加载的模块,供下一次调用 require() 使用:

local myBar = { message = "Hello from BAR!" }
local myBar2 = { message = "I've changed!" }

-- 使用 `package.loaded` 来表示我们已经加载过它了
package.loaded['bar'] = myBar
print(require('bar').message)
package.loaded['bar'] = myBar2
print(require('bar').message)

如果你需要更加动态,也可以重写 require 函数:

local myBar = {
  message = "Hello from BAR!"
}

local oldRequire = require

function require(name)
  if name == "bar" then return myBar end
  return oldRequire(name)
end

local bar = require("bar")

print("Bar says: '"..bar.message.."'")

你可以查看模块文档package.searchers 看起来很有前途,可以进行通用的加载,但我好像无法修改它以包含自己的加载器。

2020-08-28 14:54:57
用户11740758
用户11740758

我在游戏内使用 Lua 控制台执行类似于 load() 函数的东西。目的是让所需的 Lua 文件只包含字符串并返回它们。就像这样的文件:

return {
cmd=[[return function(cm) cm=io.popen(cm, 'r') cm=cm:read('a+') return cm end]]
}

然后你可以这样操作:

love.code=require('more_functions')
love.cmd=load(love.code.cmd)()

我的 require() 里还有一个安装代码将它们都放到正确的位置。我设置了一个带有已定义函数的元表,整个看起来就像这样:

-- Name: koys.lua
return {
cat=[[return function(cat) for line in io.lines(cat) do io.write(string.format('%s\n',line)) end end]],
free=[[return function() local a=(collectgarbage('count')*1024) print('Before:',a,'Byte') collectgarbage() print('After:',(collectgarbage('count')*1024),'Byte') a=a-collectgarbage('count')*1024 print('Freed:',a,'Byte') return a end]],
gauss=[[return function(gauss) return ((gauss*gauss+gauss)/2) end]],
shell=[[return function(shell) if (type(shell)=='string') then os.execute(shell) else os.execute('/bin/bash') end end]],
cmd=[[return function(cm) cm=io.popen(cm, 'r') cm=cm:read('a+') return cm end]],
len=[[return function(len) local incr=0 for _ in pairs(len) do incr=incr+1 end return incr end]],
dump=[[return function(dump) for key,value in pairs(dump) do io.write(string.format("%s=%s=%s\n",key,type(value),value)) end end]],
help=[[return function(help) love.dump(getmetatable(help).__index) end]],
printf=[[return function(prf,...) io.write(prf:format(...)) end]],
bl=[[return function(r,g,b,a) love.rrand=function() return r end love.grand=function() return g end love.brand=function() return b end love.arand=function() return a end end]],
install=[[getmetatable(love).__index.cmd=load(package.loaded.koys.cmd)()
getmetatable(love).__index.cat=load(package.loaded.koys.cat)()
getmetatable(love).__index.dump=load(package.loaded.koys.dump)()
getmetatable(love).__index.free=load(package.loaded.koys.free)()
getmetatable(love).__index.len=load(package.loaded.koys.len)()
getmetatable(love).__index.gauss=load(package.loaded.koys.gauss)()
getmetatable(love).__index.shell=load(package.loaded.koys.shell)()
getmetatable(love).__index.printf=load(package.loaded.koys.printf)()
getmetatable(love).__index.help=load(package.loaded.koys.help)()
getmetatable(love).__index.bl=load(package.loaded.koys.bl)()
getmetatable(love).__index.info=debug.getinfo
getmetatable(love).__index.cmd=load(package.loaded.koys.cmd)()
getmetatable(love).__index.concat=table.concat
getmetatable(love).__index.remove=table.remove
getmetatable(love).__index.unpack=table.unpack
getmetatable(love).__index.insert=table.insert
getmetatable(love).__index.sort=table.sort
getmetatable(love).__index.move=table.move
getmetatable(love).__index.pack=table.pack]]
}

之后只需要这样:

love.code=require('koys') -- 这个是包含 Lua 代码文本元素的表
setmetatable(love,{__index={}}) -- ...这将扩展 love...
load(love.code.install)() -- ...并结合元方法。

之后,在表 love 中你就可以通过运行以下命令来显示其内部内容:

love:dump()

或者使用以下命令来显示元表的内容:

love:help()

截屏 enter image description here

2020-08-28 16:36:42