Lua - 现有索引的__newindex元方法?

时间:2017-08-12 21:35:44

标签: indexing lua metatable global-scope meta-method

我最近在lua中了解到了美沙酮的存在,我一直在玩弄它们,直到我想到了一个想法:是否有可能使用那些试图避免"重复"在桌子上?我搜索和搜索,到目前为止无法找到我正在寻找的东西,所以我在这里。

  • 所以,我希望能够做到这一点,以及目的:

它将用于魔兽插件编程。 我想创建一个工具,在全局范围内创建变量或函数时会提示警告(以避免使用它,因为可能与其他插件发生命名冲突)。 我可以从那里做的另一件事是将所有传输重定向到_G表。因此,当用户在全局范围内创建变量或函数时,该工具会捕获它,将其存储在表中而不是_G中,并且每当用户尝试从_G访问某些内容时,该工具将首先在所述中查找它表;并仅将_G用作后备。 这样,用户将无法担心正确的封装或命名,该工具将为他完成所有这些工作。

  • 我已经设法做的事情:

我在_G上设置一个__newindex元方法来捕获全局范围的变量和函数,并在插件加载结束时删除元方法,以避免被其他插件使用。 对于_G transit"的间接,我已经知道如何在尝试使用_G之前使用__index尝试将值存储在另一个表中。

  • 问题我有:

这很有效,但只适用于_G中不存在的变量和函数。每当为一个已经在_G表中的键赋值时,它就不起作用(出于显而易见的原因)。我确实能够捕获这些情况,并且基本上不可能实际覆盖_G的内容,而是使用一种"重载" (但用户甚至不知道)。

  • 我尝试了什么:

我试图挂钩rawset,看它是否被自动调用,而且看起来不是。

我还没有找到关于lua中_G表的大量文档,因为主要是短名称。我确定某些东西必须存在于某个地方,我可能会利用这些信息按照我想要的方式完成工作,但目前我只是有点迷失并且没有想法。 所以,是的,我想知道是否有任何方式来抓住"所有"隐式调用rawset"为了在让它做它的东西之前进行一些检查。我收集到了显然没有__existingindex或其他东西的元方法,所以你知道有什么方法可以吗?

1 个答案:

答案 0 :(得分:1)

虽然您在评论中得到了答案,但Lua 5.1中对环境有更深刻的概念。环境是一个附加到功能的表,其中该功能重定向其全局'读写。 _G只是对全球'的引用。环境,即主线程(​​主协程)的环境。它可以被清除为零而没有非明显的效果,因为它只是一个变量,类似于T = { }; T._T = T

具体来说,_G == getfenv(0),除非有人改变其含义(参见getfenv()参考,其参数是什么)。加载脚本时,它会隐式绑定到全局环境。由于Lua的顶级作用域(又称主要作用域)只是一个匿名函数,因此它的环境可以随时反弹到任何其他表:

-- x.lua

local T = { }
local shadow = setmetatable({ }, { __index = getfenv(0) })

local mt = {
    __index = shadow,
    __newindex = function (t, k, v)
        -- while T is empty, this will be called every time you 'set global'
        -- do whatever you want here
        shadow[k] = v
        print(tostring(k)..' is set to '..tostring(v))
    end
}

setmetatable(T, mt) -- T[k] goes to shadow, then to _G
setfenv(1, T)       -- change the environment of this module to T

hello = "World"     -- 'hello is set to World'
print(T.hello)      -- 'World'
print(_G.hello)     -- 'nil', because we didn't even touch _G

hello = 3.14        -- 'hello is set to 3.14'
hello = nil         -- 'hello is set to nil'
hello = 2.72        -- 'hello is set to 2.72'

function f()        -- 'f is set to function: 0x804a00'
    print(hello)
end

f()                 -- '2.72'

assert(getfenv(f) == getfenv(1))
assert(getfenv(f) == T)

setfenv(f, _G)      -- return f back to _G
f()                 -- 'nil'

使用这种方法,您可以完全隐藏其他模块中的元化机制。请注意,mt调用后对setmetatable()的更改无效。

另请注意,setfenv()下面定义的所有函数共享相同的环境T(不适用于通过require加载的外部函数/模块或从这些函数返回的/模块,因为环境继承是词汇的。)

暂时在__newindex上设置_G可能有效,但请记住,您之间调用的任何函数都可能尝试设置全局变量,这可能会干扰您的逻辑或以微妙的方式破坏它们。碰撞的可能性应该很低,因为破坏_G是一个坏主意,每个人都知道。