Lua中设置table为只读属性的方法详解

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

--[[------------------------------------------------------------------------------
-** 设置table只读 出现改写会抛出lua error
-- 用法 local cfg_proxy = read_only(cfg) retur cfg_proxy
-- 增加了防重置设置read_only的机制
-- lua5.3支持 1)table库支持调用元方法,所以table.remove table.insert 也会抛出错误,
--  2)不用定义__ipairs 5.3 ipairs迭代器支持访问元方法__index,pairs迭代器next不支持故需要元方法__pairs
-- 低版本lua此函数不能完全按照预期工作
*]]
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)

测试环境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

代码思路设计:

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

__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.避免出现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,可以直接访问获得

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • Lua中对table排序实例

    lua中利用到的排序的基本上就是构造函数(table)了,为了便于和C区分开来,我俗称它为表单. 实例:(原理就是LUA集成的冒泡算法) 排序的一般姿势(对于只包含数字或者只包含字符串的简单数组) 复制代码 代码如下: table.sort(test) 扩展版 复制代码 代码如下: table.sort(test, function(a,b) return a.id<b.id end ) 实例一:值排序 1.数组模式 复制代码 代码如下: local test0 ={1,9,2,8,3,7,4,

  • Lua中使用table实现的其它5种数据结构

    lua中的table不是一种简单的数据结构,它可以作为其他数据结构的基础,如:数组,记录,链表,队列等都可以用它来表示. 1.数组 在lua中,table的索引可以有很多种表示方式.如果用整数来表示table的索引,即可用table来实现数组,在lua中索引通常都会从1开始. 复制代码 代码如下: --二维数组 n=10 m=10 arr={} for i=1,n do      arr[i]={}    for j=1,m do       arr[i][j]=i*j    end end f

  • Lua教程之弱引用table

    这次要介绍的内容比较少,就一个--弱引用table 1.无法超越人类智慧的智能--自动内存管理的缺陷 我们都知道,Lua是具备自动内存管理的,好吧,也许有些朋友不知道. 我们只管创建对象,无须删除对象(当然,对于不要的对象你需要设置一下nil值),Lua会自动删除那些被认为是垃圾的对象. 问题就出现在,什么对象才是垃圾对象,有些时候,我们很清楚某个对象是垃圾,但是,Lua却无法发现.   比如这样一个例子: 复制代码 代码如下: t = {};        -- 使用一个table作为t的ke

  • Lua中遍历数组和table的4种方法

    方法一,可以用for来遍历: 复制代码 代码如下: do      table_week = {      "w",      "e",      "r",      "t",      "y",      "u",      "i",      }        for i = 1, #table_week do          print(table_week[

  • Lua中的table浅析

    Lua的table提供了Map的功能,实现了"关联数组",并且整数.字符串甚至nil都可以作为索引/key:table没有固定的大小.   基于table,可以表示普通数组.符号表.集合.记录.队列和其他数据结构.   而Lua也是通过table来解决模块(module).包(package)和对象(Object)的. 例如io.read表示使用"read"来索引table io.   在Lua中,table既不是值也不是"变量",而是对象,可以

  • Lua中table的几种构造方式详解

    之前对于Lua的研究都是纸上谈兵,没有真正的项目练手,现在公司的项目基本上都是用Lua开发,是时候补充一下我那蹩脚的Lua知识了. 基础数据类型.表达式.循环结构什么的我就不说了,这么简单的东西说了也要被大家喷. 今天我想说说table的几种构造方式. 0.Lua调试工具--LuaEditor 首先,如果你是第一次接触Lua,请补充一下Lua的最基本之中的基础语法(小若:O O!),然后下载一个LuaEditor工具,用来查看Lua执行效果,当然也可以调试,本篇内容不解释这个工具.可以百度一下这

  • Lua table简明总结

    一. table table是lua唯一的数据结构.table 是 lua 中最重要的数据类型. table 类似于 python 中的字典.table 只能通过构造式来创建.其他语言提供的其他数据结构如array.list等等,lua都是通过table来实现的.table非常实用,可以用在不同的情景下.最常用的方式就是把table当成其他语言的数组. 实例1: 复制代码 代码如下: mytable = {} for index = 1, 100 do     mytable[index] =

  • Lua中获取table长度问题探讨

    又有同事在lua的table长度问题上犯错了,我们一起来看看吧~~~ 看以下代码: 复制代码 代码如下: local tblTest1 =  {      1,      2,      3  }    print(table.getn(tblTest1)) 这段代码输出的结果是3,这个大家都知道,是吧.不管最后那个3后面有没有加逗号,结果都是3. 再看下面的代码: 复制代码 代码如下: local tblTest2 =  {      1,      a = 2,      3,  }   

  • Lua中的table学习笔记

    table 在 Lua 里是一种重要的数据结构,它可以说是其他数据结构的基础,通常的数组.记录.线性表.队列.集合等数据结构都可以用 table 来表示,甚至连全局变量(_G).模块.元表(metatable)等这些重要的 Lua 元素都是 table 的结构.可以说,table  是一个强大而又神奇的东西. table 特性 在之前介绍 Lua 数据类型时,也说过了 table 的一些特性,简单列举如下(详情可查看之前的介绍): 1.table是一个"关联数组",数组的索引可以是数字

  • 获取Lua表结构(table)数据实例

    只是获取一个全局变量什么的太没意思了,今天我们来玩个高难度的--获取Lua表结构的数据. (旁白:O O!我是不是该说点什么?) 上一章传送门:http://www.jb51.net/article/55093.htm 1. 什么是table table是Lua里最强大的数据类型,我们可以当成是数组,但是它又和数组有点不一样,建议大家看看Lua的语法教程,因为我对table也没有熟悉到可以给大家解释的程度. (旁白:那你还写什么教程...) 2. 获取table变量 现在,我们给helloLua

随机推荐