【问题标题】:What does this piece of lua code from awesome wm do?来自 awesome wm 的这段 lua 代码有什么作用?
【发布时间】:2018-12-26 13:56:56
【问题描述】:
看看这段代码:
local urgent = {}
local capi =
{
client = client,
}
local client
do
client = setmetatable({}, {
__index = function(_, k)
client = require("awful.client")
return client[k]
end,
__newindex = error -- Just to be sure in case anything ever does this
})
end
我无法理解它的作用。它来自awesome-wm 项目。这些是我无法理解的事情:
-
client = client 在capi 的声明中
-
setmetatable 里面的东西do-end
【问题讨论】:
标签:
lua
scope
metaprogramming
declaration
awesome-wm
【解决方案1】:
-
client = client 在capi 的声明中
这是定义该文件范围内可用的 capi 部分,如果您查看 client.lua 文件,您会看到其中定义的 capi 具有客户端、鼠标、屏幕和真棒。
capi 表中定义的每个项目都有一个对应的 .c 文件。这些文件定义了诸如client 之类的对象。 urgent.lua 具有该对象的可见性,可能它是一个全局变量,这就是我们可以设置client = client 第二个客户端引用全局变量的方式。
以下是 2 个文件的示例:
main.lua
bar = "Hello World!"
local foo = require('foo')
print(foo.bar)
foo.lua
local foo = {
bar = bar
}
return foo
main.lua 中的打印函数会产生Hello World!
-
setmetatable 里面的东西do-end
在这里,通过在 do-end 块中扭曲 setmetatable,代码在受限范围内执行。通常这样做是为了包含块的局部变量,以便它们在代码执行后不会持续存在。
这不是这个块的目的,因为该块没有局部变量。在我看来,阻塞只是为了表明被修改的对象是客户端的局部变量,而不是是全局的客户变量。
另外这里的元表是用来防止循环依赖循环的,这个在项目中出现类似代码的一些地方提到了cmets,比如client.lua里面定义了local screen。
【解决方案2】:
@Nifim 的回答非常好。我只想添加更多关于为什么这段代码存在于其适当的历史背景中的上下文。在 Lua 5.2 之前,模块系统是不同的。在核心 Lua 库中定义了一个神奇的 module() 函数。当您创建一个模块时,您必须在调用 module() 之前首先创建所有全局变量的本地版本,否则它将在自己的全局环境中运行。 “capi”代表“Core API”或“C(语言)API”,具体取决于天气。如果今天使用我们现在拥有的所有知识编写了 Awesome,那么就不会有公共的“C 语言”API,它们将始终隐藏在私有部分中以增加灵活性。现在设置“c.my_own_property”会在 capi.client 和 awesome.client 之间进行几次往返,以适应所有遗留约束。
现在,元表魔法是一种称为元延迟加载的 Lua 模式。因为urgent是awful.client的子模块,所以不能直接导入awful.client而不造成循环依赖。随着时间的推移,随着 Awesome API 的定义越来越好,进行了越来越多的重构,并且它们经常引入奇怪的依赖项以保持一定程度的向后兼容性。在最好的情况下,我们会忽略所有用户配置,只是重新设计整个代码以避免这些循环依赖。但是,每次我们这样做时,上述 API 的所有用户都会在一天早上醒来,他们无法再登录到他们的计算机。因此,存在这种解决方法是为了防止此类事件,以换取一些奇怪的代码和维护负担。