高阶流程控制语句(五): 用 delimited continuation 实现 RAII
16 May 2012
什么是RAII?
RAII 就是在进入代码块的时候运行构造函数,在退出代码块的时候运行析构函数。C++ 原生支持 RAII:
class Resource {
public:
Resource() { init...; }
~Resource() { fini...; }
};
int main() {
Resource resource;
} |
RAII 的实现: using
C#有个语法糖 using,支持对 IDispose 接口的 RAII:
using (MyResource myRes = new MyResource())
{
myRes.DoSomething();
} |
展开成实际代码就是:
MyResource myRes= new MyResource();
try
{
myRes.DoSomething();
}
finally
{
if (myRes!= null) ((IDisposable)myRes).Dispose();
} |
Scala 可以自己实现 using 函数:
def using[T <: { def dispose: Unit }, R](resource: => T)(block: (T) => R) = {
var res = resource
try {
block(res)
} finally {
if (res != null) res.dispose
}
} |
使用起来也跟 C# 的方法差不多:
class Res {
println("init")
def dispose = { println("fini") }
def work = { println("work") }
}
using(new Res) { _.work } |
这个实现有个麻烦的地方,如果多层 using 套用的话,代码会乱成一坨:
using(new Res) { res1 =>
using(new Res) { res2 =>
using(new Res) { res3 =>
using(new Res) { res4 =>
doSomething
}
}
}
} |
RAII 的实现: reset/shift
现在我们用 reset/shift 实现 RAII
import scala.util.continuations._
def managed[T <: { def dispose: Unit }](resource: => T): T @suspendable =
shift { k =>
val res = resource
try {
k(res)
} finally {
res.dispose
}
} |
与之前的 using 实现类似,但是 using 使用一个闭包 block 作为参数,这里没有使用闭包作为参数,而是使用了 shift 捕获了后续的代码块,赋给 shift 的参数 k,我们先调用 k 然后再调用 dispose,可以做到析构函数类似的效果。这样 managed 函数在使用的时候也比 using 方便很多,需要做的就是在用 reset 把变量的作用域包起来:
class Res(name: String) {
println("init: " + name)
def dispose = { println("fini: " + name) }
def work = { println("work: " + name) }
}
reset {
val res = managed(new Res("X"))
res.work
} |
|
这样使用多个资源也不会出现嵌套的问题了,而且会按照初始化相反的顺序进行析构:
reset {
val res1 = managed(new Res("A"))
val res2 = managed(new Res("B"))
val res3 = managed(new Res("C"))
val res4 = managed(new Res("D"))
res1.work
res2.work
res3.work
} |
|
备注: Scala 中 continuation 的使用
从官网下载 Scala 解压后 bin 目录下有名称为 scala 的可执行文件,如果要使用 continuation 必须添加参数 -P:continuations:enable,下面是以运行本文中的例子:
$ ./scala -P:continuations:enable
Welcome to Scala version 2.9.2 (OpenJDK 64-Bit Server VM, Java 1.6.0_24).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import scala.util.continuations._
import scala.util.continuations._
scala> def managed[T <: { def dispose: Unit }](resource: => T): T @suspendable =
| shift { k =>
| val res = resource
| try {
| k(res)
| } finally {
| res.dispose
| }
| }
managed: [T <: AnyRef{def dispose: Unit}](resource: => T)T @scala.util.continuations.cpsParam[Unit,Unit]
scala> class Res(name: String) {
| println("init: " + name)
| def dispose = { println("fini: " + name) }
| def work = { println("work: " + name) }
| }
defined class Res
scala> reset {
| val res = managed(new Res("X"))
| res.work
| }
init: X
work: X
fini: X
scala> |
comments powered by Disqus