« 惰性编译资源仓库中的源文件 | 返回首页 | 一种 16 倍抗锯齿字体渲染的方法 »

lua 5.4 可能会增加 to-be-closed 特性

如果你有关注 lua 在 github 上的仓库,就会发现,最近一段时间增加了一个新特性:to-be-closed 的 local 变量。

鉴于历史上 lua 每次的大版本开发过程中都会增加很多有趣的特性,却无法保持到版本正式发布。本文也只是介绍一下这个有趣的特性,并不保证它一定会被纳入语言标准。正式的发布版中即使有这个特性,语法上也可能有所不同。

我认为 Lua 加入这个特性的动机是它缺乏 RAII 机制。过去,我们必须用 pcall 来确保一段代码运行完毕,然后再清理相关的资源。这会导致代码实现繁琐,几乎无法正确实施。比如,如果你用 C 函数申请了一块资源,期望在使用完毕后可以清除干净,过去就只能依赖 __gc 方法。但 gc 的时机不可控,往往无法及时清理。如果你把释放过程放在运行过程的末尾,是很难确定整个运行过程中没有异常跳出的可能,那样就无法执行最后的释放流程。

lua 5.4 预计要增加的 to-be-closed 特性,是允许给堆栈上的 local 变量添加一个 to-be-closed 属性。有了这个属性的 local 变量,一旦超出它的作用域,就立刻运行一段相关的代码。这很像是结合了 golang 的 defer 和 C++ 的析构函数。

在 lua 代码中,我们可以使用 local *toclose c = function(errobj) ... end 来将一个 local 变量 c 定义成 to-be-closed 的。这里 c 是一个 function ,在 c 出了作用域后,它就会被调用。这很像 golang 的 defer 。

如果正常出作用域:比如是通过 goto break return 等,函数的参数为 nil ;如果是通过 error 异常跳出,那么 error 对象就会作为第一个参数传入。

这里的 to-be-closed 变量 c 也可以是非 function 类型,这时,触发的就是它的 __close 元方法。这很接近 C++ 的析构函数的行为。不过它并不等价于析构函数,因为对象还可以继续被使用。

触发 __close 时,会多传一个参数是对象本身。有了这个机制,我们就不再担心 io.open 这种临时打开的文件无法即使关闭了,因为 FILE 对象默认会加上 __close 方法,你需要做的是在需要的地方加上 *toclose 声明。

有了 to-be-closed 后,for 迭代器就可以实现的更完备了。for 会将迭代器声明为 to-be-closed 的,像 lfs.dir 这种操作系统目录迭代器,你就不会担心是否能及时关闭目录对象了。因为无论任何原因跳出 for 循环,迭代器的 __close 方法都会被调用。

你也有方法阻止 __close 方法调用,那就是使用 coroutine 来包装一个运行过程。如果使用 yield 跳出,是不会处罚 __close 方法的。新的 coroutine 库增加了 kill 方法来一次性触发所有挂起的 __close 并可以让线程对象可复用。

to-be-closed 也可以通过 C API lua_toclose 声明。有了这个,我们可以更放心的在 C 代码中申请临时资源,而不必担心某个 api 调用会抛出 error ,导致临时资源没有释放干净。在过去,我们通常是利用 lua_newuserdata 来申请临时内存,这往往无法及时回收。

Comments

新项目似乎没有任何选旧版本的理由。
请教一下云风: 一个linux下的c++项目中引入lua,应该怎么选取lua的版本? 5.1、5.2、5.3、5.4? 云风有什么建议? 谢谢!
最新lua 5.4 beta版的to-be-closed变量的用法,大概是这样。 function test2() local t = {x=1}; setmetatable(t, {__close=function(...) print('close') end}); local tc = t;--t必须要提前指定了__close元方法 end test2();
lua真的很需要这个东东,之前项目用到很多,还特地在lua 5.1里加了个defer的语法。。 https://github.com/peete-q/lua-defer 有需要的可以拿走
不错的特性,希望Lua越来越强大,能加入更多实用的功能
用了两年的lua,云风大佬的各种库帮我解决了很多问题,万分感谢,最近在研究pbc,想实现3.0版的pb
偶尔来云大BLOG瞅瞅,终于更新了。

Post a comment

非这个主题相关的留言请到:留言本