管理外部资源(类似于C ++中的RAII?)

时间:2012-11-10 14:20:20

标签: opengl common-lisp

在常见的lisp中,管理外部资源(套接字,文件系统句柄等)的首选方法是什么?

我正在尝试在普通的lisp中制作一个简单的opengl 2d平台游戏。 问题是我不太确定如何跟踪OpenGL纹理(当不再需要时必须用glDeleteTextures删除)。

在C ++中,我更喜欢使用以下方案:

  1. 制作纹理类
  2. 为该纹理类制作智能/弱指针
  3. 将纹理存储在地图(字典/散列表)中,将纹理名称映射到纹理的弱指针。
  4. 当请求新纹理时,查看地图,看看是否有可用的非空(零)弱指针。如果可用,则返回现有对象,否则加载新纹理。
  5. 但是,我不太确定如何将此方案移植到常见的lisp,因为:

    1. 没有破坏者。
    2. 有垃圾收集器,似乎我的实现(windows平台上的clozureCL)支持终结器,但据我所知,不建议在常见的lisp中使用终结器,因为它们不是确定性的。
    3. 使用(with-*管理资源的首选方式在那里看起来不合适,因为资源可以在函数调用过程中共享和加载/卸载。
    4. 据我所知,有几种方法可供选择:

      1. 放弃自动资源管理,并手动完成。
      2. 使用宏实现类似于C ++ RAII,weakpointer和smartpointer的东西(这段代码可能不起作用):

        (defclass destructible () ())
        
        (defmethod destroy ((obj destructible)) (print (format nil "destroy: ~A" obj)))
        
        (defparameter *destructible-classes-list* nil)
        
        (defmethod initialize-instance :after ((obj destructible) &key)
          (progn
              (push *destructible-classes-list* obj)
              (print (format nil "init-instance: ~A" obj))))
        
        (defmacro with-cleanup (&rest body)
          `(let ((*destructible-classes-list* nil))
            (progn ,@body (mapcar (lambda (x) (destroy x)) *destructible-classes-list*))))
        
        (defclass refdata (destructible)
          ((value :accessor refdata-value :initform nil)
           (ref :accessor refdata-ref :initform 0)
           (weakrefcount :accessor refdata-weakref :initform 0)))
        
        (defmethod incref ((ref refdata))
          (if ref (incf (refdata-ref ref))))
        
        (defmethod decref ((ref refdata))
          (if ref
            (progn (decf (refdata-ref ref))
             (if (<= (refdata-ref ref) 0) 
               (progn (destroy (refdata-value ref))
                  (setf (refdata-value ref) nil))))))
        
        (defmethod incweakref ((ref refdata))
          (if ref (incf (refdata-weakref ref))))
        
        (defmethod decweakref ((ref refdata))
          (if ref (decf (refdata-weakref ref))))
        
        (defclass refbase (destructible) ((data :accessor refbase-data :initform nil)))
        
        (defmethod deref ((ref refbase))
          (if (and (refbase-data ref) (refdata-value (refbase-data ref)))
            (refdata-value (refbase-data ref))
            nil))
        
        (defclass strongref (refbase) ())
        
        (defclass weakref (refbase) ())
        
        (defmethod destroy :before ((obj strongref))
          (decref (refbase-data obj)))
        
        (defmethod destroy :before ((obj weakref))
          (decweakref (refbase-data obj)))
        
        (defmethod initialize-instance :after ((obj strongref) &key)
          (incref (refbase-data obj)))
        
        (defmethod initialize-instance :after ((obj weakref) &key)
          (incweakref (refbase-data obj)))
        
      3. 有更好的方法吗?

        C ++概念说明: What is a smart pointer and when should I use one?

1 个答案:

答案 0 :(得分:9)

如果要处理动态范围范围,请使用UNWIND-PROTECT。如果程序离开该范围 - 通常或出错 - 则调用清理表单。只需在那里解除分配或做任何你想做的事。

有时Lisps会使用某种“资源”机制来跟踪已使用和未使用的对象。这样的库提供了从池的快速分配,分配,初始化,解除分配,资源化对象的映射。 CLIM定义了一个原始版本:Resources。 CCL有一个类似的原始版本。

在带有'timers'的Lisp中,您可以定期运行一个函数来查找要解除分配的“对象”。在CCL中,您可以使用一个使用PROCESS-WAIT睡眠一段时间的线程。

关于你的编码风格:

  • FORMAT可以直接输出到流,不需要PRINT
  • (defmethod foo ((e bar)) (if e ...))IF毫无意义。 e永远是一个对象。
  • 不需要许多PROGN。如果需要,可以在IF替换WHEN时将其删除。