Common Lisp CFFI:指向指针的指针

Common Lisp CFFI: pointer to the pointer

我正在尝试为 Sundials CVODE 库编写 CFFI 包装器。 SWIG 在日晷 headers 上感到窒息,因为它们相互关联,而 SWIG 找不到合适的 headers,所以我手工完成了:有点费力,但我做到了。

现在我正在尝试测试它是否正常工作。现在,只需简单地创建 "problem object" 并删除它。这就是问题的开始。因此,"problem object" 是通过函数

分配的
SUNDIALS_EXPORT void *CVodeCreate(int lmm, int iter);

我为此创建了包装器:

(cffi:defcfun "CVodeCreate" :pointer
  (lmm :int)
  (iter :int))

PS。 SUNDIALS_EXPORT(至少在 Unix 上)基本上没什么。

现在,要销毁 object,日晷使用它自己的函数:

SUNDIALS_EXPORT void CVodeFree(void **cvode_mem);

因此,我需要将对 CVodeCreate 创建的 object 的引用传递给它。在 C 中,如果我的记忆没有错误,我会做类似 CVodeFree(&problem_object) 的事情。在 CL 中,我为函数编写了这个包装器:

(cffi:defcfun "CVodeFree" :void
  (cvode-mem :pointer))

所以,这里的COVDE-MEM是一个指向指针的指针。问题是如何获取CL/CFFI中指针的指针?这是代码的开头:

(defvar *p* (cvodecreate 1 2))

(PS。不要担心传递给CVODECREATE的数字,它们只是告诉使用哪些方法,仍然需要定义常量以使其更具可读性)

所以 *P* 类似于

#.(SB-SYS:INT-SAP #X7FFFE0007060)

如果我直接将它传递给 CVODEFREE,它最终会出错:

CL-USER> (cvodefree *p*)
; Evaluation aborted on #<SIMPLE-ERROR "bus error at #X~X" {1005EC9BD3}>.

我试过传递 (CFFI:POINTER-ADDRESS *P*) 但结果类似 "bus error..." (甚至不确定这个函数 returns 是否是我需要的)。我也尝试过 (CFFI:MAKE-POINTER (CFFI:POINTER-ADDRESS *P*)),但还是没有成功。

建议采用这种方法:

(cffi:with-foreign-object (p :pointer)
           (setf (cffi:mem-ref p :pointer) (cvodecreate 1 2))
           (cvodefree p))

这有效(至少它不会引发错误)。我想我理解它是如何工作的:它创建(分配内存)一个 pointer-to-a-pointer P,它的 MEM-REF(或者在 C 术语中将取消引用 *p)被填充根据 CVODECREATE 上的结果。最后,我将此 pointer-to-a-pointer 传递给 CVODEFREE,它正是期望如此。最后,表单完成后,分配给 P 的内存将被释放。这是正确的方法吗?这是我唯一能接受的吗?

是的,你的方法看起来不错,这里有一个小测试来展示可以运行直接来自 repl 的概念。

(let* (;; a float
       (v0 32s0)

       ;; a pointer to a float foreign memory
       (p0 (cffi:foreign-alloc :float :initial-element v0))) 

  ;; a new pointer
  (cffi:with-foreign-object (p1 :pointer)

    ;; make the new pointer point to the first pointer
    (setf (cffi:mem-aref p1 :pointer) p0)

    ;; dereferencing twice should give you the original number
    (cffi:mem-aref (cffi:mem-aref p1 :pointer) :float)))

p.s。我相信你现在已经知道了,很抱歉花了这么长时间才给你答复。希望这可以帮助其他人