这段来自awesome wm的 Lua 代码是做什么用的?

看一下这段代码:

local urgent = {}

local capi =
{
    client = client,
}

local client
do
    client = setmetatable({}, {
        __index = function(_, k)
            client = require("awful.client")
            return client[k]
        end,
        __newindex = error -- 以防万一
    })
end

我对它的理解有些困难。它来自于awesome-wm项目。以下是我理解困难的部分:

  1. 在声明capi时使用了client = client

2.在do-end内使用的setmetatable内容

点赞
用户7396148
用户7396148
  1. 在“capi”的声明中使用“client = client”

这定义了文件范围内“capi”的哪一部分是可用的。如果查看“client.lua”文件,则会看到其中定义的“capi”具有客户端、鼠标、屏幕和Awesome。

对于在“capi”表中定义的每个项,都有一个相应的.c文件。这些文件定义对象,例如“客户机”。urgent.lua可以看到这个对象,很可能是一个全局变量,这就是我们可以设置client = client的第二个客户端引用全局变量的方式。

下面是两个文件的示例:

main.lua

bar = “Hello World!”

local foo = require('foo')

print(foo.bar)

foo.lua

local foo = {
    bar = bar
}
返回foo

main.lua中的打印函数将导致输出“Hello World!”

  1. setmetatabledo-end中的内容

通过将setmetatable包裹在一个do-end块中,代码在受限范围内执行。通常这样做是为了包含块的局部变量,使它们不能在代码执行后持续存在。

虽然如此,但这个块的目的并不是如此,因为该块没有局部变量。就我看来,阻止只是为了表明修改的对象是client的局部变量而不是client的全局变量

此外,metatable在这里用于防止循环依赖循环,这在类似代码出现在项目中的某些地方的注释中有提到,例如在client.lua中定义本地屏幕的地方。

2018-12-26 17:34:47
用户1672598
用户1672598

@Nifim的回答非常优秀。我只是想在适当的历史背景下添加更多的内容来解释为什么这段代码存在。在Lua5.2之前,模块系统是不同的。Lua核心库中定义了一个魔术的module()函数。当您创建模块时,您必须先创建所有全局变量的本地版本,然后再调用module(),否则它将在自己的全局环境中运行。"capi"代表"Core API"或"C (语言) API",具体取决于天气。如果Awesome是用我们现在所拥有的所有知识编写的,就不会有一个公共的"C语言"API,它们总是隐藏在私有部分以增加灵活性。现在,设置"c.my_own_property"需要在capi.client和awful.client之间进行几次往返,以适应所有遗留约束。

现在,元表的魔法是一种Lua模式,称为元懒加载。因为"urgent"是"awful.client"的子模块,所以它不能直接导入"awful.client",否则会导致循环依赖。随着Awesome API不断完善,进行了越来越多的重构,通常引入了一些奇怪的依赖关系来保持某种程度的向后兼容性。在最好的情况下,我们应该忽略所有用户配置,重新设计整个代码以避免这些循环依赖关系。但是,每次我们这样做时,所有使用该API的用户都会在一天早上醒来,发现他们无法登录到计算机。因此,这种解决方法存在以避免此类事件,并换取一些奇怪的代码和维护负担。

2018-12-26 19:18:43