Lisp 计划中的RAII?

Lisp 计划中的RAII?,lisp,scheme,raii,Lisp,Scheme,Raii,在方案中是否有实现资源获取的方法 我知道RAII在GC-ed语言中不能很好地工作(因为我们不知道对象是如何被销毁的)。然而,Scheme有一些很好的东西,比如延续、动态风和闭包——有没有一种方法可以将这些结合起来来实现RAII 如果不是,那么设计人员如何设计他们的代码以不使用RAII [我遇到的一个常见示例如下: 我有一个3D网格,我有一个顶点缓冲区对象, 当网格不再使用时,我希望释放VBO。] 谢谢 如果这只是一次性的,您可以编写一个围绕dynamic wind的宏,在thunks前后进行设置

在方案中是否有实现资源获取的方法

我知道RAII在GC-ed语言中不能很好地工作(因为我们不知道对象是如何被销毁的)。然而,Scheme有一些很好的东西,比如延续、动态风和闭包——有没有一种方法可以将这些结合起来来实现RAII

如果不是,那么设计人员如何设计他们的代码以不使用RAII

[我遇到的一个常见示例如下:

我有一个3D网格,我有一个顶点缓冲区对象, 当网格不再使用时,我希望释放VBO。]


谢谢

如果这只是一次性的,您可以编写一个围绕
dynamic wind
的宏,在thunks前后进行设置和拆卸(我假设
allocate vertex buffer object
free vertex buffer object
是您的构造函数和析构函数):

如果这是一种您经常使用的模式,对于不同类型的对象,您可以编写一个宏来生成这种宏;您可能希望一次分配一系列绑定,因此您可能希望在开始时有一个绑定列表,而不仅仅是一个绑定列表

这是一个即兴的,更一般的版本;我不太确定它的名字,但它展示了它的基本思想(在原始版本中编辑为修复无限循环):

你可以这样使用它:

(with-managed-objects ((vbo (allocate-vertex-buffer-object 1 2 3)
                            (free-vertext-buffer-object vbo))
                       (frob (create-frobnozzle 'foo 'bar)
                             (destroy-frobnozzle frob)))
  ;; do stuff ...
  )
下面的示例演示了它的工作原理,包括使用continuations退出和重新进入作用域(这是一个相当做作的示例,如果控制流有点难以遵循,请道歉):

应打印:

entering foo entering bar inside foo: 1 bar: 2 exiting bar exiting foo * Let's try that again! entering foo entering bar exiting bar exiting foo * All done 输入foo 进入酒吧 在…内 傅:1 酒吧:2间 退出酒吧 退出foo *让我们再试一次! 输入foo 进入酒吧 退出酒吧 退出foo *全部完成
call/cc
只是当前延续的
call的缩写
;如果您的计划没有较短的表格,请使用较长的表格

更新:正如您在评论中所阐明的,您正在寻找一种方法来管理可以从特定动态上下文返回的资源。在这种情况下,您必须使用终结器;终结器是一个函数,一旦GC证明无法从其他任何地方访问它,它将随对象一起调用。终结器不是标准的,但大多数成熟的方案系统都有终结器,有时使用不同的名称。例如,在PLT方案中,请参见

您应该记住,在Scheme中,可以重新输入动态上下文;这与大多数其他语言不同,在这些语言中,可以使用异常在任意点退出动态上下文,但不能重新进入。在上面的示例中,我演示了一种天真的方法,即在您离开动态上下文时使用
dynamic wind
释放资源,如果您再次进入,则重新分配资源。这可能适用于某些资源,但对于许多资源来说并不合适(例如,重新打开一个文件,当您重新进入动态上下文时,您现在将处于文件的开头),并且可能会有很大的开销

Taylor Campbell(是的,有一个关系)已经(2009-03-28条目)解决了这个问题,并根据您想要的确切语义提出了一些备选方案。例如,他提供了一个
unwind protext
表单,在无法重新进入可访问资源的动态上下文之前,该表单不会调用清理过程


因此,这涵盖了许多不同的选项。由于Scheme是一种非常不同的语言,并且具有非常不同的约束条件,所以它与RAII没有精确的匹配。如果您有一个更具体的用例,或者您简要提到的关于该用例的更多细节,我可能会为您提供一些更具体的建议。

嗨,anon。我想知道我的回答是否让您满意,或者您是否在寻找其他东西。我认为您的回答与给出的方案一样好。在某种程度上,我们必须知道模型何时“消亡”并放弃vbo。然而,在RAII+GC中,我们不需要事先知道这一点,我们可以说“模型,我不知道你什么时候会死;但我知道当你死了,你会放弃VBO”。我们不能完全做后者,因为方案是gc-ed的;我原本希望的是。。。是一种聪明的宏麦克,它会自动地插入某种类型的REF计数,这就提供了RAII+ REQUATE的类型。为了进一步说明这一点,考虑下面的情况:我们创建一个模型,我们不知道它什么时候被删除,但是我们知道它被渲染了很多;所以我们给它一个VBO;传来传去。。。当没有人使用它时,它会释放VBO。代码中没有一个地方我知道“我现在可以释放模型了。”啊,好的。我不知道你所指的RAII的确切方面;这个术语有点模糊,可以用于分配具有定义良好的生命周期的资源,并安全地处理异常,这就是我所讨论的。如果需要具有任意生存期的对象,则需要使用某种终结器;该标准不涉及终结,但大多数相对成熟的方案都有一些定义终结器的方法。我将扩展我的答案,以解决您的问题。
(with-managed-objects ((vbo (allocate-vertex-buffer-object 1 2 3)
                            (free-vertext-buffer-object vbo))
                       (frob (create-frobnozzle 'foo 'bar)
                             (destroy-frobnozzle frob)))
  ;; do stuff ...
  )
(let ((inner-continuation #f))
  (if (with-managed-objects ((foo (begin (display "entering foo\n") 1) 
                                  (display "exiting foo\n")) 
                             (bar (begin (display "entering bar\n") (+ foo 1)) 
                                  (display "exiting bar\n")))
        (display "inside\n")
        (display "foo: ") (display foo) (newline)
        (display "bar: ") (display bar) (newline)
        (call/cc (lambda (inside) (set! inner-continuation inside) #t)))
    (begin (display "* Let's try that again!\n") 
           (inner-continuation #f))
    (display "* All done\n")))
entering foo entering bar inside foo: 1 bar: 2 exiting bar exiting foo * Let's try that again! entering foo entering bar exiting bar exiting foo * All done