Lua 設置table爲只讀屬性

若是轉載,請註明博文來源:http://www.cnblogs.com/vanishfan/p/6909153.html  望各位支持!html

 

項目中部分只讀表易被人誤改寫,故決定在非線上環境裏對這些表附加只讀屬性,方便在出現誤改寫的時候拋出lua錯誤,最終版代碼以下:app

 1 --[[------------------------------------------------------------------------------
 2 -** 設置table只讀 出現改寫會拋出lua error
 3 -- 用法 local cfg_proxy = read_only(cfg)  retur cfg_proxy
 4 -- 增長了防重置設置read_only的機制
 5 -- lua5.3支持 1)table庫支持調用元方法,因此table.remove table.insert 也會拋出錯誤,
 6 --               2)不用定義__ipairs 5.3 ipairs迭代器支持訪問元方法__index,pairs迭代器next不支持故須要元方法__pairs
 7 -- 低版本lua此函數不能徹底按照預期工做
 8 *]]
 9 function read_only(inputTable)
10     local travelled_tables = {}
11     local function __read_only(tbl)
12         if not travelled_tables[tbl] then
13             local tbl_mt = getmetatable(tbl)
14             if not tbl_mt then
15                 tbl_mt = {}
16                 setmetatable(tbl, tbl_mt)
17             end
18 
19             local proxy = tbl_mt.__read_only_proxy
20             if not proxy then
21                 proxy = {}
22                 tbl_mt.__read_only_proxy = proxy
23                 local proxy_mt = {
24                     __index = tbl,
25                     __newindex = function (t, k, v) error("error write to a read-only table with key = " .. tostring(k)) end,
26                     __pairs = function (t) return pairs(tbl) end,
27                     -- __ipairs = function (t) return ipairs(tbl) end,   5.3版本不須要此方法
28                     __len = function (t) return #tbl end,
29                     __read_only_proxy = proxy
30                 }
31                 setmetatable(proxy, proxy_mt)
32             end
33             travelled_tables[tbl] = proxy
34             for k, v in pairs(tbl) do
35                 if type(v) == "table" then
36                     tbl[k] = __read_only(v)
37                 end
38             end
39         end
40         return travelled_tables[tbl]
41     end
42     return __read_only(inputTable)
43 end

測試代碼以下:函數

 1 local t0 = {k = 1}
 2 local t2 = {
 3     fdsf = {456}
 4 }
 5 local t1 = {
 6         a = {456, 89},
 7         b = {456,ddss = 9, t2 = t2},
 8         d = 45,
 9         e = "string",
10 }
11 t1.c=t1
12 
13 local t3 = read_only(t1)
14 
15 print(t3.d, t3.c.e, t3.c.c.b.t2.fdsf)
16 function q1() t3.d = 4555 end
17 function q2() t3.c.d = 90 end
18 function q3() t3.c.c.b.t2.fdsf =90 end
19 function q4() table.remove(t3.a) end
20 function q5() t3.b[ddss] = nil end
21 function q6() t3[f] = 89 end
22 function q7() table.insert(t3.a, 999) end
23 
24 print(pcall(q1))
25 print(pcall(q2))
26 print(pcall(q3))
27 print(pcall(q4))
28 print(pcall(q5))
29 print(pcall(q6))
30 print(pcall(q7))
31 print(t3.a[1])
32 for k,v in pairs(t3) do
33     print("===pairs t3:",k,v)
34 end
35 for k,v in pairs(t3.a) do
36     print("===pairs t3.a:",k,v)
37 end
38 for k,v in ipairs(t3) do
39     print("===ipairs t3:",k,v)
40 end
41 for k,v in ipairs(t3.a) do
42     print("===ipair t3.a",k,v)
43 end
44 print("len t3:",#t3)
45 print("len t3.a:", #t3.a)
46 
47 local t4 = read_only(t2)
48 
49 local t5 = read_only(t0)
50 local t6 = read_only(t0)
51 
52 print(t3.b.t2, read_only(t2))
53 print(t5, t6, t0)

 

測試環境https://www.lua.org/cgi-bin/demo  lua5.3.4:測試

45    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
456
===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

 

 

代碼思路設計:this

1.使用proxy={}空表而不是目標表tbl來設置__newindex是由於__newindex必須在原表裏面不存在纔會調用,這樣就依然能夠對已存在的字段進行改寫lua

__newindexThe 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.spa

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 tablekey, 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.避免出現table的互相引用,加入travelled_tables存儲已經設置過proxy的table的映射code

3.對於原表tbl的訪問使用__index=tbl

4.對於表查長度使用__len= function () return #tbl end

5.對於遍歷pairs,查到lua5.3的pairs默認迭代器next不支持訪問元表__index,故直接__pairs = function () return pairs(tbl) end,以此來生成對目標表的迭代遍歷

6.對於ipairs,查到lua5.3 ipairs函數生成的迭代器默認就支持訪問元表__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.避免重複建立read_only,每一個tbl只建立一個proxy代理,在tbl的metatable裏和proxy的metatable裏都設置屬性__read_only_proxy,能夠直接訪問得到

相關文章
相關標籤/搜索