xie*_*pan 5 sbcl common-lisp ffi
我正在尝试使用 SBCL 的 FFI 工具调用 C 函数。C 函数需要字节缓冲区(无符号字符)并处理字节数据。不幸的是,在大多数情况下,缓冲区非常大。我想知道我是否已经有一个 Lisp 字节向量(simple-array元素类型为 '(unsigned-byte 8)),是否可以将其传递给 C 函数,而不是分配外来缓冲区并在那里复制字节。我认为这是一个经常出现的场景,也许 SBCL 团队已经涵盖了这一点。
我查看了 SBCL 手册,它只指出sb-alien:c-string类型可以直接传递给 C 函数而不进行复制,并且它要求字符为“base-char”(在 0 ~ 127 范围内,支持 unicode)。我们也可以在不复制的情况下传递字节向量,这不是很好吗?
我不知道这在SBCL中是否可行。为了使其成为可能,至少需要以 GC 不移动数组的方式分配数组。SBCL 手册包含对可能执行此操作的宏的引用sb-sys:with-pinned-objects,但那里似乎没有任何其他信息。
然而,当我需要做这样的事情时,我会向后做:在 C 级别创建一个合适的对象,然后用 Lisp 代码包装它来访问它。这样做的优点是你知道如何从 C 语言中处理它,缺点是它不是数组,所以你必须编写自己的访问函数:诸如此类的东西read-sequence不起作用等等。
我最终使用了包装外来向量的结构:这是我用来处理有符号字节数组的代码块:
(deftype index ()
`(integer 0 (,most-positive-fixnum)))
...
(defstruct (foreign-octet-vector
(:constructor %make-foreign-octet-vector (octets size)))
(octets (error "no")
:type (alien (* (signed 8)))
:read-only t)
(size 0
:type index
:read-only t))
(defun make-foreign-octet-vector (size)
(%make-foreign-octet-vector
(make-alien (signed 8) size)
size))
(defun free-foreign-octet-vector (v)
(free-alien (foreign-octet-vector-octets v))
nil)
(declaim (inline fov-ref (setf fov-ref)))
(defun fov-ref (v n)
(declare (type foreign-octet-vector v)
(type index n))
(if (< n (foreign-octet-vector-size v))
(deref (foreign-octet-vector-octets v) n)
(error "out of range")))
(defun (setf fov-ref) (value v n)
(declare (type foreign-octet-vector v)
(type index n)
(type (signed-byte 8) value))
(if (< n (foreign-octet-vector-size v))
(setf (deref (foreign-octet-vector-octets v) n) value)
(error "out of range")))
Run Code Online (Sandbox Code Playgroud)
最重要的是,有一个相当明显的宏用于unwind-protect确保事物被释放。
我不知道这是否是特殊的 SBCL 代码,或者实际上总体上是正确的,但它对我有用。CFFI 可能为这一切提供了一些更好的接口。