我该如何检测Lua脚本访问全局变量?

我开始在一个有点混乱的 C++/Lua 代码库中工作,当我在应用程序执行过程中转储 _G 的内容时,有数百个变量我确定只在某个地方初始化,但在代码中不再使用。为了清理这个问题,我想设置一个机制,每当 Lua 访问全局变量时记录日志。

这是我的想法如何实现这一点 - 我想设置一个代理 _G,它只会通过 __index__newindex 将所有读写访问传递到其自己的原始 _G 的副本。然而,这个简单的脚本不起作用,只输出:

C:\Programs\lua-5.1.5_Win32_bin\lua5.1: error in error handling

GProx =
{
    vars = _G
}

setmetatable(GProx, {

    __index = function (t, name)

        print("Read> " .. name)
        return t.vars[name]

    end,

    __newindex = function (t, name, val)

        print("Write> " .. name .. ' = ' .. val)
        t.vars[name] = val

    end

})

setfenv(0, GProx)

a = 1 --> 应该输出 'Write> a'
print(a) --> 应该输出 'Read> print', 'Read> a' 和 '1'

这种方法好吗,还是有更好的方法?

如果这是一个有效的思路,那么我的代码片段中有什么问题?

点赞
用户869951
用户869951

你可以直接在_G表上设置元表,详见PIL第14.2节,因此你非常接近了。网上还有一些现有的Lua模块可以实现这一点(也许penlight包含一个)。

2014-06-18 12:08:30
用户3080396
用户3080396

试试这段代码,它可用于读和写:

do
    -- 使用本地变量
    local old_G, new_G = _G, {}

    -- 如果想要消除有关已设置的字段(例如预声明的全局变量)的日志记录,请复制值。
    -- for k, v in pairs(old_G) do new_G[k] = v end

    setmetatable(new_G, {
        __index = function (t, key)
            print("Read> " .. tostring(key))
            return old_G[key]
        end,

        __newindex = function (t, key, val)
            print("Write> " .. tostring(key) .. ' = ' .. tostring(val))
            old_G[key] = val
        end,
    })

    -- 在第 1 级(顶层函数)上设置它
    setfenv(1, new_G)
end

以下是更改的摘要:

  • 使用块来引用旧的 _G。在您提出的实现中,如果设置了名为 vars 的全局变量,它将覆盖 GProx.vars 并破坏代理。
  • 在打印之前,应将 keyval 传递给 tostring,因为大多数值(即表)不会自动转换为字符串。
  • 在第 1 级设置环境通常足够,并且不会干扰 Lua 的内部工作。
2014-06-18 12:13:48