zoukankan      html  css  js  c++  java
  • Lua 设置table为只读属性

    如果转载,请注明博文来源:http://www.cnblogs.com/vanishfan/p/6909153.html  望各位支持!

    项目中部分只读表易被人误改写,故决定在非线上环境里对这些表附加只读属性,方便在出现误改写的时候抛出lua错误,最终版代码如下:

     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

    代码思路设计:

    1.使用proxy={}空表而不是目标表tbl来设置__newindex是因为__newindex必须在原表里面不存在才会调用,这样就依然可以对已存在的字段进行改写

    __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.

    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的映射

    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,可以直接访问获得

  • 相关阅读:
    C语言点滴
    随便记点什么
    STL的使用
    Linux下OTG支持USB摄像头
    socket编程实战-调试
    socket编程实战-bind端口占用问题
    socket编程实战-tcp_tw_recycle问题
    socket编程实战-connect超时问题
    BT[3]-BLE广播详解
    BT[2]-BLE初体验:心率计
  • 原文地址:https://www.cnblogs.com/vanishfan/p/6909153.html
Copyright © 2011-2022 走看看