CFFI、回调和 void *(如何在公共 lisp/cffi 中创建指向本机对象的指针?)

and*_*rew 5 common-lisp

假设我在 C 语言的物理库中有一个回调,它为附近/接触的两个对象生成碰撞信息。这个回调需要一个 (void *)data 参数,我想用它来传递我的一个 lisp 对象。

我如何使用这个 (void *) 参数?据我所知(我对 CFFI 比较陌生,但很快就掌握了窍门)我可以

  1. 以某种方式将对象(CLOS 对象)转换为指针并将此指针传递给回调。我试过了,(cffi:convert-to-foreign my-obj :pointer)但它只返回 my-obj,而不是指针,我不能用它调用回调。
  2. 在回调周围创建一个闭包,以便它可以在不需要 void* 指针的情况下引用数据。我在谷歌上搜索了很多,但没有找到任何有关此的信息。我可以将 defcallback 包装在 defun 中吗?
  3. 将我想传递的对象推入全局范围。这看起来真的,真的很脏,我想不惜一切代价避免它。

关于跨平台方式的任何想法?我知道 C 中的很多回调都采用 void* 参数,所以一定有人想到了这一点。我正在使用 Clozure CL,但就像我说的,跨平台/跨实现越多越好。谢谢!

who*_*isp 2

我不确定这是否有效。如果 GC 移动您在闭包中捕获的对象,那将非常难看。也许您必须禁用 GC。 http://www.sbcl.org/manual/Calling-Lisp-From-C.html#Calling-Lisp-From-C

也许一个选择是从回调中调用另一个 Lisp 函数来设置变量,但要使其正常工作,Lisp 函数的地址不应更改。我不知道 lisp 函数的地址是否会在垃圾回收期间或重新定义函数时更改。

回调不可移植: http://common-lisp.net/project/cffi/manual/html_node/Tutorial_002dCallbacks.html

如果您的实现支持回调,您可以在这里查看: http://common-lisp.net/project/cffi/manual/html_node/Implementation-Support.html#Implementation-Support

我修改了 CFFI 帮助中的一个示例。回调函数是比较运算符,它调用一个函数来打印它所做的每个比较。您可以将其推入全局变量(或闭包),而不是打印列表 PAR。

请注意,我不相信这种用法是保存的。如果由于某种原因 STORE 在内存中的位置发生变化并且回调函数中的代码没有相应更新,则可能会导致错误的结果。

(require :cffi)

(defpackage :run
  (:use :cl :cffi))
(in-package :run)

(defcfun "qsort" :void
    (base :pointer)
    (nmemb :int)
    (size :int)
    (fun-compar :pointer))


(defun store (par)
  (format t "I'm comparing ~a~%" par))


(defcallback < :int ((a :pointer) (b :pointer))
    (let ((x (mem-ref a :int))
          (y (mem-ref b :int)))
      (store (list x y))
      (cond ((> x y) 1)
            ((< x y) -1)
            (t 0))))

(with-foreign-object (array :int 10)
  ;; Initialize array.
  (loop for i from 0 and n in '(7 2 10 4 3 5 1 6 9 8)
     do (setf (mem-aref array :int i) n))
          ;; Sort it.
  (qsort array 10 (foreign-type-size :int) (callback <))
  ;; Return it as a list.
  (loop for i from 0 below 10
     collect (mem-aref array :int i)))
Run Code Online (Sandbox Code Playgroud)

这是示例的输出:

I'm comparing (7 2)
I'm comparing (4 3)
I'm comparing (10 3)
I'm comparing (10 4)
I'm comparing (2 3)
I'm comparing (7 3)
I'm comparing (7 4)
I'm comparing (7 10)
I'm comparing (5 1)
I'm comparing (9 8)
I'm comparing (6 8)
I'm comparing (1 6)
I'm comparing (5 6)
I'm comparing (2 1)
I'm comparing (2 5)
I'm comparing (3 5)
I'm comparing (4 5)
I'm comparing (7 5)
I'm comparing (7 6)
I'm comparing (7 8)
I'm comparing (10 8)
I'm comparing (10 9)
Run Code Online (Sandbox Code Playgroud)

我在 freenode #lisp 通道日志中搜索回调:

http://ccl.clozure.com/irc-logs/lisp/2010-04/lisp-2010.04.28.txt
Best comment there:
12:56:53 <pkhuong> and if you find out that threads are indeed an issue, I'd code a C wrapper to build a message queue.
Run Code Online (Sandbox Code Playgroud)

也许这些代码示例已被更正(我现在无法检查,因为服务器已关闭):

http://paste.lisp.org/display/98482
http://paste.lisp.org/display/98495
Run Code Online (Sandbox Code Playgroud)