項目中部分只讀表易被人誤改寫,故決定在非線上環(huán)境里對這些表附加只讀屬性,方便在出現(xiàn)誤改寫的時候拋出lua錯誤,最終版代碼如下:
--[[------------------------------------------------------------------------------
-** 設(shè)置table只讀 出現(xiàn)改寫會拋出lua error
-- 用法 local cfg_proxy = read_only(cfg) retur cfg_proxy
-- 增加了防重置設(shè)置read_only的機制
-- lua5.3支持 1)table庫支持調(diào)用元方法,所以table.remove table.insert 也會拋出錯誤,
-- 2)不用定義__ipairs 5.3 ipairs迭代器支持訪問元方法__index,pairs迭代器next不支持故需要元方法__pairs
-- 低版本lua此函數(shù)不能完全按照預(yù)期工作
*]]
function read_only(inputTable)
local travelled_tables = {}
local function __read_only(tbl)
if not travelled_tables[tbl] then
local tbl_mt = getmetatable(tbl)
if not tbl_mt then
tbl_mt = {}
setmetatable(tbl, tbl_mt)
end
local proxy = tbl_mt.__read_only_proxy
if not proxy then
proxy = {}
tbl_mt.__read_only_proxy = proxy
local proxy_mt = {
__index = tbl,
__newindex = function (t, k, v) error("error write to a read-only table with key = " .. tostring(k)) end,
__pairs = function (t) return pairs(tbl) end,
-- __ipairs = function (t) return ipairs(tbl) end, 5.3版本不需要此方法
__len = function (t) return #tbl end,
__read_only_proxy = proxy
}
setmetatable(proxy, proxy_mt)
end
travelled_tables[tbl] = proxy
for k, v in pairs(tbl) do
if type(v) == "table" then
tbl[k] = __read_only(v)
end
end
end
return travelled_tables[tbl]
end
return __read_only(inputTable)
end
測試代碼如下:
local t0 = {k = 1}
local t2 = {
fdsf = {456}
}
local t1 = {
a = {456, 89},
b = {456,ddss = 9, t2 = t2},
d = 45,
e = "string",
}
t1.c=t1
local t3 = read_only(t1)
print(t3.d, t3.c.e, t3.c.c.b.t2.fdsf)
function q1() t3.d = 4555 end
function q2() t3.c.d = 90 end
function q3() t3.c.c.b.t2.fdsf =90 end
function q4() table.remove(t3.a) end
function q5() t3.b[ddss] = nil end
function q6() t3[f] = 89 end
function q7() table.insert(t3.a, 999) end
print(pcall(q1))
print(pcall(q2))
print(pcall(q3))
print(pcall(q4))
print(pcall(q5))
print(pcall(q6))
print(pcall(q7))
print(t3.a[1])
for k,v in pairs(t3) do
print("===pairs t3:",k,v)
end
for k,v in pairs(t3.a) do
print("===pairs t3.a:",k,v)
end
for k,v in ipairs(t3) do
print("===ipairs t3:",k,v)
end
for k,v in ipairs(t3.a) do
print("===ipair t3.a",k,v)
end
print("len t3:",#t3)
print("len t3.a:", #t3.a)
local t4 = read_only(t2)
local t5 = read_only(t0)
local t6 = read_only(t0)
print(t3.b.t2, read_only(t2))
print(t5, t6, t0)
測試環(huán)境https://www.lua.org/cgi-bin/demo lua5.3.4:
string table: 0x20d4ba0
false input:17: error write to a read-only table with key = d
false input:17: error write to a read-only table with key = d
false input:17: error write to a read-only table with key = fdsf
false input:17: error write to a read-only table with key = 2
false input:17: error write to a read-only table with key = nil
false input:17: error write to a read-only table with key = nil
false input:17: error write to a read-only table with key = 3
===pairs t3: e string
===pairs t3: b table: 0x20ccd60
===pairs t3: a table: 0x20d4e70
===pairs t3: d 45
===pairs t3: c table: 0x20ca700
===pairs t3.a: 1 456
===pairs t3.a: 2 89
===ipair t3.a 1 456
===ipair t3.a 2 89
len t3: 0
len t3.a: 2
table: 0x20d4870 table: 0x20d4870
table: 0x20d5690 table: 0x20d5690 table: 0x20d1140
代碼思路設(shè)計:
1.使用proxy={}
空表而不是目標(biāo)表tbl來設(shè)置__newindex是因為__newindex必須在原表里面不存在才會調(diào)用,這樣就依然可以對已存在的字段進行改寫
__newindex: The indexing assignment table[key] = value. Like the index event, this event happens when table is not a table or when key is not present in table. The metamethod is looked up in table.
Like with indexing, the metamethod for this event can be either a function or a table. If it is a function, it is called with table, key, and value as arguments. If it is a table, Lua does an indexing assignment to this table with the same key and value. (This assignment is regular, not raw, and therefore can trigger another metamethod.)
Whenever there is a __newindex metamethod, Lua does not perform the primitive assignment. (If necessary, the metamethod itself can call rawset to do the assignment.)
2.避免出現(xiàn)table的互相引用,加入travelled_tables存儲已經(jīng)設(shè)置過proxy的table的映射
3.對于原表tbl的訪問使用__index=tbl
4.對于表查長度使用__len= function () return #tbl end
5.對于遍歷pairs,查到lua5.3的pairs默認(rèn)迭代器next不支持訪問元表__index,故直接__pairs = function () return pairs(tbl) end
,以此來生成對目標(biāo)表的迭代遍歷
6.對于ipairs,查到lua5.3 ipairs函數(shù)生成的迭代器默認(rèn)就支持訪問元表__index,故不需要添加__ipairs
8.2 – Changes in the Libraries
•The ipairs iterator now respects metamethods and its __ipairs metamethod has been deprecated.
7.對于table.insert
, table.remove
不用特殊處理,lua5.3的table lib支持元表操作,故依然會拋錯
8.2 – Changes in the Libraries
•The Table library now respects metamethods for setting and getting elements.
8.避免重復(fù)創(chuàng)建read_only,每個tbl只創(chuàng)建一個proxy代理,在tbl的metatable里和proxy的metatable里都設(shè)置屬性__read_only_proxy,可以直接訪問獲得
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
您可能感興趣的文章:- 深入談?wù)刲ua中神奇的table
- Lua Table轉(zhuǎn)C# Dictionary的方法示例
- Lua編程示例(一):select、debug、可變參數(shù)、table操作、error
- 舉例講解Lua中的Table數(shù)據(jù)結(jié)構(gòu)
- Lua table中安全移除元素的方法
- Lua的table庫函數(shù)insert、remove、concat、sort詳細介紹
- C++遍歷Lua table的方法實例
- Lua中釋放table占用內(nèi)存的方法
- Lua中table的遍歷詳解
- Lua中獲取table長度問題探討
- Lua中獲取table長度的方法
- Lua中table里內(nèi)嵌table的例子
- Lua面向?qū)ο缶幊讨A(chǔ)結(jié)構(gòu)table簡例