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

Common lisp CFFI、回调和void*(如何在公共lisp/CFFI中创建指向本机对象的指针?),common-lisp,Common Lisp,假设我在C语言的一个物理库中有一个回调函数,它为两个相邻/接触的对象生成碰撞信息。这个回调使用一个(void*)数据参数,我想用它传入一个lisp对象 如何使用此(void*)参数?根据我的想法(我对CFFI比较陌生,但很快就掌握了窍门),我可以 以某种方式将对象(CLOS对象)强制转换为指针,并将该指针传递给回调。我尝试了(cffi:convert to foreign my obj:pointer),但它只返回我的obj,而不是指针,我无法用它调用回调 围绕回调创建一个闭包,这样回调就可以引

假设我在C语言的一个物理库中有一个回调函数,它为两个相邻/接触的对象生成碰撞信息。这个回调使用一个(void*)数据参数,我想用它传入一个lisp对象

如何使用此(void*)参数?根据我的想法(我对CFFI比较陌生,但很快就掌握了窍门),我可以

  • 以某种方式将对象(CLOS对象)强制转换为指针,并将该指针传递给回调。我尝试了
    (cffi:convert to foreign my obj:pointer)
    ,但它只返回我的obj,而不是指针,我无法用它调用回调
  • 围绕回调创建一个闭包,这样回调就可以引用数据,而不需要void*指针。我在谷歌搜索了很多次,没有找到任何关于这个的信息。我能把defun包起来吗
  • 将我要传递的对象推入全局范围。这看起来真的,真的很脏,我想不惜一切代价避免它

  • 有没有跨平台方式的想法?我知道很多C语言中的回调都使用void*参数,所以一定有人想到了这一点。我正在使用Clozure CL,但正如我所说,越是跨平台/跨实现越好。谢谢

    我不确定这是否有效。如果GC移动您在闭包中捕获的对象,那将非常糟糕。也许你必须禁用GC。

    也许可以选择从回调函数中调用另一个Lisp函数来设置变量,但为了实现这一点,Lisp函数的地址不应更改。我不知道lisp函数的地址是否会在垃圾收集期间或重新定义函数时发生更改

    回调不可移植:

    如果您的实现支持回调,则可以查看此处:

    我修改了CFFI帮助中的一个示例。回调函数是比较运算符,它调用一个函数来打印它执行的每个比较。而不是打印列表Par,你可以把它推到一个全局变量(或闭包)。 请注意,我不相信这种用法是安全的。如果由于某种原因,存储在内存中的位置发生了变化,并且回调函数中的代码没有相应地更新,那么可能会导致不良结果

    (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)))
    
    我在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.
    

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

    这是有道理的(关于垃圾收集的注释)。我的实现确实支持回调。有没有办法将回调封装在闭包中,这样它就可以访问所需的数据,还是会受到相同问题的影响?我不明白调用另一个lisp函数来设置变量是什么意思。你能举个例子吗?@andrew实际上我刚刚意识到调用另一个lisp函数是愚蠢的。最好的办法是与freenode上的#lisp频道的人讨论这些问题。这本身就是一个好答案,我没有听说freenode上的lisp频道。现在我只有(defparameter my value nil),在调用回调之前将其设置为my object,回调可以很好地访问它。就像我说的,这样做会让我觉得很肮脏,但在我找到更好的方法之前,我必须这样做。谢谢你的帮助!至于GC部分:有一些FFI将锁定函数,但折衷的是,您必须显式释放它。在我看来,这是一个很好的折衷办法,因为我认为处理回调(通常)并不常见,而且可以创建closured回调(这是dope),这是有道理的。如果没有某种全局变量,似乎没有一个好的方法来实现它。我讨厌globals,但如果这是唯一的方法,那么好的文档和有限的使用就可以了。
    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