计划中的 RAII?
无论如何,有没有办法在方案中实现资源获取和初始化?
我知道 RAII 在 GC 语言中效果不佳(因为我们不知道对象何时被销毁)。然而,Scheme 有一些不错的东西,比如延续、动态风和闭包——有没有办法使用这些东西的组合来实现 RAII?
如果没有,策划者如何设计他们的代码以不使用 RAII?
[我遇到的一个常见示例如下:
我有一个 3D 网格,我有一个附加到它的顶点缓冲区对象, 当不再使用 Mesh 时,我希望释放 VBO。]
谢谢!
Is there anyway to implement Resource Acquisation is Initialization in Scheme?
I know that RAII does not work well in GC-ed languages (since we have no idea whe the object is destroyed). However, Scheme has nice things like continuations, dynamic-wind, and closures -- is there a way to use some combination of this to implement RAII?
If not, how do schemers design their code to not use RAII?
[A common example I run into is the following:
I have a 3D mesh, I have a Vertex Buffer Object atached to it,
when the Mesh is no longer used, I want the VBO freed up.]
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果这只是一次性的,您总是可以编写一个围绕
dynamic-wind
的宏,在重击之前和之后进行设置和拆卸(我假设allocate-vertex-buffer-object
和free-vertex-buffer-object
是您的构造函数和析构函数):如果这是您针对不同类型的对象经常使用的模式,你可以编写一个宏来生成这种宏;并且您可能希望一次分配一系列这些,因此您可能希望在开始时有一个绑定列表,而不是只有一个。
这是一个即兴的、更通用的版本;我不太确定这个名字,但它演示了基本思想(编辑以修复原始版本中的无限循环):
您可以按如下方式使用它:
这是一个演示其工作原理的示例,包括使用延续退出和重新进入范围(这是一个相当人为的示例,如果控制流有点难以遵循,我们深表歉意):
这应该打印:
call/cc
只是一个缩写调用当前延续
;如果您的方案没有较短的形式,请使用较长的形式。更新:正如您在评论中澄清的那样,您正在寻找一种方法来管理可以从特定动态上下文返回的资源。在这种情况下,您将不得不使用终结器;终结器是一个函数,一旦 GC 证明无法从其他任何地方访问该对象,该函数就会与您的对象一起调用。终结器不是标准的,但大多数成熟的Scheme系统都有它们,有时以不同的名称。例如,在 PLT 方案中,请参阅遗嘱和执行人。
你应该记住,在Scheme中,动态上下文是可以重新输入的;这与大多数其他语言不同,在大多数其他语言中,您可以使用异常在任意点退出动态上下文,但无法重新进入。在上面的示例中,我演示了一种简单的方法,即在离开动态上下文时使用
dynamic-wind
释放资源,并在再次进入时重新分配资源。这可能适合某些资源,但对于许多资源来说并不适合(例如,重新打开文件,当您重新进入动态上下文时,您现在将位于文件的开头),并且可能有显着的开销。Taylor Campbell(是的,有关系)他的博客中有一篇文章(2009- 03-28 条目)解决这个问题,并根据您想要的确切语义提出一些替代方案。例如,他提供了一个 unwind-protext 形式,该形式不会调用清理过程,直到不再可能重新进入可访问资源的动态上下文为止。
因此,这涵盖了许多可用的不同选项。与 RAII 没有完全匹配,因为Scheme 是一种非常不同的语言并且具有非常不同的约束。如果您有更具体的用例,或者您简要提到的用例的更多详细信息,我也许可以为您提供一些更具体的建议。
If this is just a one-off, you could always just write a macro that wraps around
dynamic-wind
, doing the setup and teardown in the before and after thunks (I'm assuming thatallocate-vertex-buffer-object
andfree-vertex-buffer-object
are your constructor and destructors here):If this is a pattern that you use a lot, for different types of objects, you might write a macro to generate this sort of macro; and likely you are going to want to allocate a series of these at a time, so you may want to have a list of bindings at the beginning, instead of just a single one.
Here's an off-the cuff, more general version; I'm not really sure about the name, but it demonstrates the basic idea (edited to fix infinite loop in original version):
And you would use this as follows:
Here's an example that demonstrates it working, including exiting and re-entering the scope using continuations (this is a rather contrived example, apologies if the control flow is a bit hard to follow):
This should print:
call/cc
is simply an abbreviation ofcall-with-current-continuation
; use the longer form if your Scheme does not have the shorter one.Update: As you clarified in your comments, you are looking for a way to manage resources that can be returned out of a particular dynamic context. In this case, you're going to have to use a finalizer; a finalizer is a function that will be called with you object once the GC has proven that it cannot be reached from anywhere else. Finalizers are not standard, but most mature Scheme systems have them, sometimes under different names. For instance, in PLT Scheme, see Wills and Executors.
You should keep in mind that in Scheme, a dynamic context can be re-entered; this differs from most other languages, in which you may be able to exit a dynamic context at any arbitrary point using exceptions, but you cannot re-enter. In my example above, I demonstrated a naïve approach of using
dynamic-wind
to deallocate resources when you leave the dynamic context, and re-allocate them if you enter again. This may be appropriate for some resources, but for many resources it would not be appropriate (for instance, re-opening a file, you will now be at the beginning of the file when you re-enter the dynamic context), and may have significant overhead.Taylor Campbell (yes, there is a relation) has an article in his blag (the 2009-03-28 entry) addressing this issue, and presenting a few alternatives based on the exact semantics that you want. For instance, he provides an
unwind-protext
form that will not call the cleanup procedure until it is no longer possible to re-enter the dynamic context in which the resource is accessible.So, that covers a lot of different options that are available. There is no exact match to RAII, as Scheme is a very different language and has very different constraints. If you have a more specific use case, or more details on the use case that you mentioned briefly, I may be able to provide you with some more specific advice.