【发布时间】:2014-09-18 13:07:14
【问题描述】:
我有一个 C API,我试图通过 JNA API 在 Clojure 中使用。我的问题可以通过以下示例得到最好的证明。假设我在库中有这个 C 代码:
typedef struct {
int foo;
int bar;
double baz;
} MyStruct;
MyStruct createStruct() {
MyStruct myStruct;
myStruct.foo = 3;
myStruct.bar = 4;
myStruct.baz = 3.14;
return myStruct;
}
double addStruct(MyStruct myStruct) {
return myStruct.foo + myStruct.bar + myStruct.baz;
}
在本例中,我想调用createStruct,然后将结果传递给addStruct。这里的重点是MyStruct 是按值传递 作为返回类型和参数。在任何时候我都不需要实际读取MyStruct 中字段的值。
另外,在我的系统中,原生函数是这样包装的:
; `quux` is defined in `some-lib` and returns an `int`
(let [fn- (com.sun.jna.Function/getFunction "some-lib" "quux")]
(fn [& args]
(.invoke fn- Integer (to-array args))))
目标是获得一个类型来替代上面的Integer,它将MyStruct 包装为一个值。
我发现涵盖此主题的唯一资源是 this article,但它只讨论了如何通过引用传递结构。
鉴于此,以下是我尝试采取的不同方法来解决此问题:
-
创建一个继承自
Structure的类,即JNA的built-in mechanism for creating and using structs。鉴于该页面上的信息,我尝试仅使用 Clojure 创建以下类:class MyStruct extends Structure implements Structure.ByValue { int foo; int bar; double baz; }deftype不适用于这种情况,因为该类需要从抽象类Structure继承,而gen-class不起作用,因为该类需要具有公共非静态字段@987654339 @、bar和baz。据我所知,没有一个标准的 Clojure Java 互操作工具可以创建上述类。
创建一个继承自
Structure的类并覆盖结构字段 getter/setter 方法。由于gen-class是(我相信)唯一允许直接继承的 Clojure 构造,并且它不支持多个公共非静态字段,因此下一个替代方案是根本不使用字段。 Looking at theStructureabstract class documentation,似乎有可能使用“虚拟”结构字段的覆盖混合物,这样它就可以真正从不同的来源(such as thestatefield fromgen-class)获取和设置数据。浏览文档,似乎覆盖了readField、writeField,以及其他一些方法可能有预期的效果,但我不清楚如何通过阅读文档来做到这一点,我网上找不到类似的例子。使用不同的存储类。 JNA 有无数用于包装原生类型的类。我想知道,除了定义和使用
Structure类之外,我是否可以使用另一个可以采用任意位数的泛型类(例如Integer如何容纳4 位宽的任何东西,而不管是什么类型源'实际上'是)。例如,是否可以说一个函数返回一个长度为 16 的字节数组(因为sizeof(MyStruct)是 16)?在实现NativeMapped的容器类中包装一个固定大小的数组怎么样?我也找不到如何做的例子。
【问题讨论】:
-
JNA 以
com.sun.jna.Memory的形式提供块内存分配。从那里,您可以使用Pointer.getXXX()方法从内存中的任意偏移中提取任意类型。 -
@technomage 使用
Memory作为createStruct的返回类型返回一个Pointer,其地址为0x0000000400000003。换句话说,它将结构的原始内存值视为指针。取消引用时(通过任何read或getXXX方法),它会使 VM 崩溃。 -
问题在于
struct参数和返回值语义因编译器而异,并且本机代码实际上使用Structure布局来确定如何正确设置堆栈。我建议您可以使用Memory将任意内存块分配给现有的Structure.ByValue实例。 -
你最好问问 clojure 的人。 JNA 需要在实际参数和库函数映射签名上都使用
Structure.ByValue标记,以便将结构按值传递到本机代码。 -
如果您确实以某种方式绕过定义公共字段,您需要为您的结构覆盖(受包保护的)
getTypeInfo(),它会返回 FFI 类型信息你的结构(它本身就是一个结构)。
标签: java struct clojure jna pass-by-value