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

时间:2011-07-03 21:13:30

标签: common-lisp

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

如何使用此(void *)参数?从我能想到的(我对CFFI相对较新,但很快掌握它)我可以

  1. 以某种方式将对象(一个CLOS对象)转换为指针,并将此指针传递给回调。我已经尝试了(cffi:convert-to-foreign my-obj :pointer),但它只返回my-obj,而不是指针,我无法用它调用回调。
  2. 围绕回调创建一个闭包,这样它就可以引用数据而不需要void *指针。我搜索了谷歌一堆,没有找到任何关于此的信息。我可以在defun中包装defcallback吗?
  3. 推送我想要传递到全局范围的对象。这看起来真的很脏,我想不惜一切代价避免它。
  4. 有关跨平台方式的任何想法吗?我知道C中的很多回调都带有void *参数,所以有人必须想到这一点。我正在使用Clozure CL,但就像我说的那样,跨平台/跨实现越多越好。谢谢!

2 个答案:

答案 0 :(得分:3)

很抱歉,如果我的回答有点晚,但我最近遇到了同样的问题。我使用的解决方案是使用整数键将所有要通过回调访问的lisp对象存储在哈希表中。我所要做的就是将密钥传递给c函数,如果需要,将其转换为void *。

答案 1 :(得分: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)))

这是示例的输出:

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)

我搜索了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.

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

http://paste.lisp.org/display/98482
http://paste.lisp.org/display/98495