2014-06-18 57 views
2

我開始使用C++/Lua代碼庫,這個代碼有點亂,當我在應用程序執行過程中轉儲_G的內容時,我確信有數百個變量只在某處初始化,但沒有被使用代碼中的其他任何地方。爲了清除這個問題,我想設置一個機制,只要Lua訪問一個全局變量就會記錄。如何檢測Lua腳本何時訪問全局變量?

這是我如何實現這個想法 - 我通過__index__newindex想設置一個代理_G,只會通過所有讀寫訪問沿着自身的原_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 --> Expected to print 'Write> a' 
print(a) --> Expected to print 'Read> print', 'Read> a', and '1' 

這是一個很好的做法還是有更好的方式來做到這一點?
如果這是一個有效的思路,那麼我的代碼段有什麼問題?

回答

3

試試這個片段相反,它將工作與讀取和寫入:

do 
    -- Use local variables 
    local old_G, new_G = _G, {} 

    -- Copy values if you want to silence logging 
    -- about already set fields (eg. predeclared globals). 
    -- 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, 
    }) 

    -- Set it at level 1 (top-level function) 
    setfenv(1, new_G) 
end 

這裏的變化破敗:

  • 塊用於有一個本地引用舊的_G。在您提出的實現中,如果設置了名爲vars的全局變量,則它將覆蓋GProx.vars並中斷代理。
  • keyval應該在打印前經過tostring,因爲大多數值(即表格)都不會隱式轉換爲字符串。
  • 設置1級別的環境通常就足夠了,不會混淆Lua的內部工作。
+0

謝謝,這完全符合我的需要。重要的部分是在'1'而不是'0'的環境中設置新的環境 - 即使我的原始片段開始按預期工作,當我改變它。 – electroLux

3

您可以直接在_G表上設置metatable,如PIL section 14.2所述,因此您非常接近。在網上也有一些現有的Lua模塊可以做到這一點(也許penlight包含一個)。

+0

直接在_G上設置metatable的問題是隻有在請求的鍵不存在於索引表本身中時纔會調用__index metamethod。我的代碼庫中有許多變量在C++代碼中初始化,但只能在Lua代碼中讀取 - 我不能用這種方式捕捉。 – electroLux