Взгляните на этот код:
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
client = client
in the declaration ofcapi
Это определяет, какая часть capi доступна в области видимости этого файла. Если вы посмотрите на файл client.lua, вы увидите, что определенный в нем capi
имеет клиент, мышь, экран и awesome.
Для каждого элемента, определенного в таблице 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
stuff insidedo-end
Здесь, искажая setmetatable
в блоке до конца, код выполняется в ограниченном объеме. Обычно это делается для хранения локальных переменных блока, чтобы они не сохранялись после выполнения кода.
Тем не менее, это не является целью этого блока, поскольку блок не имеет локальных переменных. На мой взгляд, блокировка просто показывает, что модифицируемый объект - это локальная переменная клиента, а нет - глобальная переменная клиента.
Кроме того, метатаблица здесь используется для предотвращения циклических циклов зависимости, это упоминается в комментариях в некоторых местах, где в проекте появляется аналогичный код, например, client.lua
, где определен local screen
.
@Nifim ответ отличный. Я просто хочу добавить больше контекста в Зачем, этот код существует в его надлежащем историческом контексте. До Lua 5.2 модульная система была другой. В основной библиотеке Lua была определена волшебная функция module()
. Когда вы создали модуль, вы имел сначала создадите локальную версию всех глобальных переменных перед вызовом module()
, потому что в противном случае он работал бы в своей собственной глобальной среде. «capi» означает «Core API» или «C (язык) API» в зависимости от погоды. Если бы Awesome был написан сегодня со всеми знаниями, которые у нас есть, не было бы общедоступного API «языка C», и они всегда были бы скрыты в частном разделе для повышения гибкости. Прямо сейчас установка "c.my_own_property" выполняет пару круговых обходов между capi.client и awful.client только для того, чтобы учесть все устаревшие ограничения.
Теперь метатаблица - это шаблон Lua под названием meta-lazy-loading. Поскольку urgent
является подмодулем awful.client
, он не может напрямую импортировать awful.client
, не вызывая циклической зависимости. Со временем, когда API-интерфейсы Awesome стали лучше определяться, проводилось все больше и больше рефакторинга, и они часто вводили странные зависимости для поддержания некоторой степени обратной совместимости. В лучшей вселенной мы бы проигнорировали конфигурацию всех пользователей и просто перепроектировали весь код, чтобы избежать этих циклических зависимостей. Однако каждый раз, когда мы это делаем, все пользователи упомянутых API просыпаются однажды утром и больше не могут войти в свой компьютер. Таким образом, существует обходной путь для предотвращения таких событий в обмен на какой-то странный код и бремя обслуживания.