Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/jsf-2/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Common lisp 使用显式资源管理初始化数据结构时是否进行了适当的错误处理?_Common Lisp - Fatal编程技术网

Common lisp 使用显式资源管理初始化数据结构时是否进行了适当的错误处理?

Common lisp 使用显式资源管理初始化数据结构时是否进行了适当的错误处理?,common-lisp,Common Lisp,当初始化一个数据结构或对象时,它的子对象在使用后需要显式释放过程,我应该如何处理初始化过程中的错误 让我举一个例子,初始化SUBOBJ1和SUBOBJ2插槽的对象,将外部指针设置为int值: (defun init-object () (let ((obj (make-object))) (setf (subobj1 obj) (cffi:foreign-alloc :int) (subobj2 obj) (cffi:foreign-alloc :int))

当初始化一个数据结构或对象时,它的子对象在使用后需要显式释放过程,我应该如何处理初始化过程中的错误

让我举一个例子,初始化SUBOBJ1和SUBOBJ2插槽的对象,将外部指针设置为int值:

(defun init-object ()
  (let ((obj (make-object)))
    (setf (subobj1 obj) (cffi:foreign-alloc :int)
          (subobj2 obj) (cffi:foreign-alloc :int))
    obj))
如果我们在SUBOBJ2插槽的外部分配中出错,我们应该为SUBOBJ1插槽执行外部释放以避免内存泄漏

作为一个想法,我可以写如下:

(defun init-object ()
  (let ((obj (make-object)))
    (handler-case
        (setf (subobj1 obj) (cffi:foreign-alloc :int)
              (subobj2 obj) (cffi:foreign-alloc :int))
      (condition (c)   ; forcedly handling all conditions
        (when (subobj1 obj) (cffi:foreign-free (subobj1 obj)))
        (error c)))    ; invoke the condition c again explicitly
    obj))
你有没有更好的想法,或者通常的惯用模式

谢谢


在回答之后,我添加了一个使用UNWIND-PROTECT的代码。这将不起作用,因为即使成功完成所有分配,也会运行释放表单

(defun init-object ()
  (let ((obj (make-object)))
    (unwind-protect
      (progn
        (setf (subobj1 obj) (cffi:foreign-alloc :int)
              (subobj2 obj) (cffi:foreign-alloc :int))
        obj)
      ; foreign pointers freed even when successfully initialized
      (when (subobj2 obj) (cffi:foreign-free (subobj2 obj)))
      (when (subobj1 obj) (cffi:foreign-free (subobj1 obj))))))
使用。当错误导致退出范围时,
解除保护
允许您强制执行清理表单

大概是这样的:

(defun init-object ()
  (let ((obj (make-object)))
    (unwind-protect
        (setf (subobj1 obj) (cffi:foreign-alloc :int)
              (subobj2 obj) (cffi:foreign-alloc :int))
      (unless (and (subobj2 obj) (subobj1 obj))
        (when (subobj1 obj) (cffi:foreign-free (subobj1 obj)))
        (when (subobj2 obj) (cffi:foreign-free (subobj2 obj)))))
    obj))
(defun init-object ()
  (let ((obj (make-object)))
    (handler-bind
        (;; forcedly handling all conditions
         (condition #'(lambda (c)
                        (declare (ignore c))
                        (when (subobj1 obj) (cffi:foreign-free (subobj1 obj)))
                        ;; return normally, allowing the condition to go up the handler chain
                        ;; and possibly to the debugger, if none exits non-locally
                        )))
      (setf (subobj1 obj) (cffi:foreign-alloc :int)
            (subobj2 obj) (cffi:foreign-alloc :int)))
    obj))

使用任何可用的方法来检测插槽是否已绑定。上面假设未初始化的插槽的值为
NIL

I第二个Rainer建议:
我将
展开保护
表单包装在宏中,并检查protected子句中的初始化是否成功。

有人建议使用
展开保护
。这是处理资源分配的惯用方法。但是,如果您的目标是在发生错误时释放资源,但如果所有操作都成功,则返回这些资源,则可以使用类似以下的方法:

(defun init-object ()
  (let ((obj (create-object)))
    (handler-case
        (progn
          (setf (subobj1 obj) (cffi:foreign-alloc :int))
          (setf (subobj2 obj) (cffi:foreign-alloc :int))
          obj)
      (error (condition)
        (free-object obj)
        ;; Re-throw the error up in the call chain
        (error condition)))))

(defun free-object (obj)
  (when (subobj2 obj) (cffi:foreign-free (subobj2 obj)))
  (when (subobj1 obj) (cffi:foreign-free (subobj1 obj))))
实现相同功能的另一种方法是进行检查,以验证是否已达到函数的结尾,如果未达到,则释放对象。然而,我并不真的喜欢这种风格,因为它并不能很好地展示正在发生的事情

但是,请注意,当您使用函数
INIT-OBJECT
时,需要将其包含在
UNWIND-PROTECT
中。否则,一旦函数返回的对象被GC'ed,您将泄漏资源

执行此操作的方法是在使用此功能时始终执行以下操作:

(let ((obj (init-object)))
  (unwind-protect
      ... use object here ...
    (free-object obj)))

另一个解决方案是在对象被GC’ed时释放它。没有标准的方法来做这件事,但是必要的功能被抽象在
trial-GARBAGE:FINALIZE
函数中。

Common Lisp具有与当今语言(如Java、C#)异常和资源管理语句相对应的功能,例如,使用
catch
和/或
finally
尝试

Common Lisp中的
try
-
catch
是通过实现的,正如您在代码中所做的那样。可以简单地重新标记相同的错误,但是您不会在实际发生错误的调试器上捕获错误。Java在创建异常时包含异常的堆栈跟踪。C#包括抛出异常时异常的堆栈跟踪。在任何情况下,我认为这两种方法都可以抛出一个新异常和一个内部异常,这样您就可以获得原始的stacktrace

公共Lisp中的
try
-
最后
是用实现的。第一个表单正常执行,其余的无条件执行,不管第一个表单是否正常返回

CommonLisp有一个功能,允许在发出错误信号的点运行代码。关于
处理程序case
的主要区别在于,如果没有处理程序非本地退出,它不会倒带堆栈,也不会阻止错误弹出到其他处理程序或调试器

因此,您可以使用如下内容:

(defun init-object ()
  (let ((obj (make-object)))
    (unwind-protect
        (setf (subobj1 obj) (cffi:foreign-alloc :int)
              (subobj2 obj) (cffi:foreign-alloc :int))
      (unless (and (subobj2 obj) (subobj1 obj))
        (when (subobj1 obj) (cffi:foreign-free (subobj1 obj)))
        (when (subobj2 obj) (cffi:foreign-free (subobj2 obj)))))
    obj))
(defun init-object ()
  (let ((obj (make-object)))
    (handler-bind
        (;; forcedly handling all conditions
         (condition #'(lambda (c)
                        (declare (ignore c))
                        (when (subobj1 obj) (cffi:foreign-free (subobj1 obj)))
                        ;; return normally, allowing the condition to go up the handler chain
                        ;; and possibly to the debugger, if none exits non-locally
                        )))
      (setf (subobj1 obj) (cffi:foreign-alloc :int)
            (subobj2 obj) (cffi:foreign-alloc :int)))
    obj))
我建议您不要匹配
条件
,因为所有条件都继承自它,例如
存储条件
。你可能不想在你无法或不可能恢复的情况下做任何事情


仅供参考,Common Lisp中的完整
try
-
catch
-
finally
子句是通过
展开保护
处理程序案例周围实现的:

(unwind-protect
     (handler-case
         (do-something)
       (error-type-1 ()
         (foo))
       (error-type-2 (e)
         (bar e)))
  (cleanup-form-1)
  (cleanup-form-2))

在这种情况下,UNWIND-PROTECT不适用,因为成功分配的外部分配值必须在init对象之后使用。对于释放对象,或者释放对象,UNWIND-PROTECT工作得很好。如果init成功,我会检查UNWIND-PROTECT protected子句,如果不成功,则取消分配所有分配的内容。如果代码发出适当错误的信号(在所有情况下),那么编写处理程序就可以了。如果你想要一些奢侈的东西,你可以写一个宏(带有checked foreign allocation(obj…),它提供了一个语言结构。我可以问更多的细节吗?我在问题中添加了一个使用UNWIND-PROTECT的代码。你的意思是这样的吗?谢谢。你的第一种方法和我的相同。你的第二种方法是我刚才要尝试的,效果很好(但是,正如您所说,由于检查是否已到达函数的末尾,因此样式不是很好)。当然,我附上了代码,其中分配的对象用于展开保护。没问题。关于琐碎垃圾:FINALIZE,我认为这是另一种好方法。我对这个话题太紧张了吗?我不这么认为。这是一件值得思考的事,因为它有助于您了解对象的生命周期。一旦您对它感到满意,您就会能够编写这种代码而不需要考虑太多。好的,我认为应该严格考虑对象的生命周期,所以我想制作一些模式来应用于这种情况。谢谢,HANDLER-BIND是我想要的。我将检查它的详细描述,并与条件匹配使用它。只有在某些情况下才会评估清理表单发生错误时,即使如此,条件层叠过程也不会发生prevented@masayukitakagi正是如此。
handler case
的CLHS条目中的注释显示了使用
handler bind
的等效实现,其中控制首先非本地传输,从而防止信号进入处理程序链,r