2017-08-12 57 views
1

我最近在lua學習了美式餐桌的存在,並且我正在與他們一起玩耍,直到有一個想法出現在我的腦海中:是否有可能使用這些來嘗試避免餐桌上的「重複」?我搜索和搜索,到目前爲止找不到我在找什麼,所以我在這裏。Lua - 現有索引的__newindex metamethod?

  • 因此,這裏就是我會希望能夠做的,目的:

這是一個在魔獸世界的插件程序使用。 我想製作一個工具,當在全局範圍中創建一個變量或函數時(爲了避免使用它,因爲可能與其他插件可能發生的命名衝突),會提示警告。 我想從那裏做的另一件事是將所有過境重定向到_G表。因此,當用戶在全局範圍內創建變量或函數時,該工具會捕獲該變量或函數,並將其存儲在表中而不是_G,並且每當用戶嘗試訪問_G中的某些內容時,該工具將首先在其中查找表;並只使用_G作爲後備。這樣,用戶就不用擔心正確的封裝或命名,該工具將爲他處理所有這些。

  • 什麼,我已經成功地做到:

我設置上_G一個__newindex元方法趕上了全球範圍的變量和函數,並在插件的加載結束移除元方法,以避免被其他插件使用。 對於「_G transit的間接」,我已經知道如何在嘗試使用_G之前使用__index來嘗試賦值存儲在另一個表中。我遇到

  • 問題:

這種運作良好,但只有那些尚未_G存在的變量和函數。無論何時爲已經在_G表中的鍵賦值,它都不起作用(出於顯而易見的原因)。我希望確實能夠抓住這些情況,基本上不可能真正覆蓋_G的內容,而是使用某種「超載」(但用戶不必知道這些)。

  • 我的嘗試:

我試圖鉤rawset,看它是否被自動調用,看來事實並非如此。

我一直無法找到關於lua中_G表的大量文檔,因爲主要是簡稱。我肯定某處必須有東西存在,我可能會使用這些信息以我想要的方式完成任務,但目前我只是有點失落並且沒有想法。 所以是的,我想知道是否有任何方法可以「捕捉」所有的「隱式調用rawset」,以便在做它的東西之前做一些檢查。我認爲這顯然沒有用於__existingindex的metamethod或其他東西,所以你知道有什麼辦法可以嗎?

+2

你在找什麼被稱爲代理表。您需要創建一個新的空表並將其分配給'_G',同時將'__newindex'和'__index'設置爲搜索以某種方式捕獲的原始_G表的函數。你的'__newindex'函數不應該允許重新定義現有的密鑰。我從來沒有做過這樣的事情,所以我不能告訴你這種解決方案有多穩定。這肯定會很慢。如果你願意,我可以做一個完整的寫作。編輯:看看'strict.lua'的靈感實現。 – ktb

+0

感謝您的回答;但我不知道它將如何解決我的問題。 能夠知道_G中是否已經存在一個密鑰不是問題(我可以簡單地在我的工具文件中有一個本地_G並檢查local_G [key]〜= nil);問題是我不知道如何知道現有密鑰何時被重新分配。 我的工具的目標是讓它「初始化並忘記」。我不想在每次嘗試向全局範圍指定某個東西時調用某個函數,我希望該工具在每次執行時都能「檢測」,因此它可以告訴我不要這樣做 – Thex

+0

如果它在某種程度上可以做到所以每當有事情被宣佈爲全球性的時候,它就會進入與_G不同的表格,那將會很棒。我知道我可以通過在每個文件的開始處獲取本地「假」_G來做到這一點,但這是我想避免必須做的 – Thex

回答

0

儘管你已經在評論中得到了答案,但在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' 

通過這種方法可以完全隱藏其他模塊元表機制。請注意,在調用setmetatable()後,對mt的更改不起作用。

還記得所有功能下面setfenv()共享相同的環境T定義(即不適用於外部的功能/經由require加載或從這些功能/模塊返回模塊,因爲環境繼承是詞彙)。

設置__newindex_G暫時可以工作,但請記住,您調用的任何函數都可能嘗試設置全局變量,這可能會干擾您的邏輯或以微妙的方式破壞邏輯。雖然衝突的概率應該很低,因爲損壞_G是一個壞主意,每個人都知道。