是否可以将字节向量传递给C函数而不在SBCL中复制

问题描述 投票:0回答:2

我正在尝试使用其 FFI 工具从 SBCL 调用 C 函数。 C 函数需要字节缓冲区(无符号字符)并处理字节数据。不幸的是,在大多数情况下,缓冲区非常大。我想知道我是否已经有一个 Lisp 字节向量(元素类型为 '(unsigned-byte 8) 的

simple-array
),是否可以将其传递给 C 函数,而不是分配外来缓冲区并复制字节那里。我认为这是一个经常出现的场景,也许 SBCL 团队已经涵盖了这一点。

我查看了 SBCL 手册,它只指出

sb-alien:c-string
类型可以直接传递给 C 函数而不进行复制,并且它要求字符为“base-char”(在 0 ~ 127 范围内,支持 unicode)。我们也可以在不复制的情况下传递字节向量,这不是很好吗?

common-lisp ffi sbcl
2个回答
2
投票

我不知道这在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")))

除此之外,还有一个相当明显的宏,使用 
unwind-protect

来确保事物被释放。

我不知道这是否是特殊的 SBCL 代码,或者实际上总体上是正确的,但它对我有用。 CFFI 可能为这一切提供了一些更好的接口。


0
投票
sb-kernel:get-lisp-obj-address

,并且您可能还需要

sb-sys:with-pinned-objects
。另一个问题是它的想法有多明智(可移植性、sbcl 中数组布局的内部变化......)。
肮脏的例子(忽略固定,long vs. int,...):

让我们创建一个使用 Lisp 数组的函数:

extern char test(const char *t, int idx) { return t[idx]; }

然后(在创建并加载共享库之后)我们可以将向量的地址作为整数传递:

(sb-alien:alien-funcall (sb-c-call:extern-alien "test" (function sb-alien:int sb-alien:unsigned-long sb-alien:unsigned-long)) (sb-kernel:get-lisp-obj-address (make-array 5 :element-type '(unsigned-byte 8) :initial-contents '(10 20 30 40 50))) 3)

将返回第三个元素(而不是第四个元素,因为从零开始)。对象有点复杂。是的,内容发生了变化,因为 lisp 对象有点复杂。例如,您可以在 -7 地址处获取数组大小(标记)。

© www.soinside.com 2019 - 2024. All rights reserved.