Java 使用JNA在Clojure中按值获取和传递结构
我试图通过JNAAPI在Clojure中使用一个C API。下面的例子最能说明我的问题。假设我在库中有此C代码:Java 使用JNA在Clojure中按值获取和传递结构,java,struct,clojure,jna,pass-by-value,Java,Struct,Clojure,Jna,Pass By Value,我试图通过JNAAPI在Clojure中使用一个C API。下面的例子最能说明我的问题。假设我在库中有此C代码: typedef struct { int foo; int bar; double baz; } MyStruct; MyStruct createStruct() { MyStruct myStruct; myStruct.foo = 3; myStruct.bar = 4; myStruct.baz = 3.14;
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
包装为一个值。
我找到的关于这个主题的唯一资源是,但它只讨论了如何通过引用传递结构
考虑到这一点,以下是我试图采取的不同方法来解决这个问题:
class MyStruct extends Structure implements Structure.ByValue {
int foo;
int bar;
double baz;
}
deftype
不适用于此场景,因为类需要从抽象类结构继承,而gen类
不起作用,因为类需要具有公共非静态字段foo
、bar
和baz
据我所知,任何标准Clojure Java互操作工具都无法创建上述类
结构的类,并重写结构字段getter/setter方法。由于gen class
是(我相信)唯一允许直接继承的Clojure构造,并且它不支持多个公共非静态字段,因此下一个选择就是根本不使用字段,使用“virtual”结构字段似乎有多种覆盖,因此它可以从不同的源()获取和设置数据。翻阅文档,似乎覆盖了readField
、writeField
,以及其他一些方法可能会产生预期的效果,但我在阅读文档时不清楚如何做到这一点,我在网上找不到任何类似的例子
结构
类,我是否可以使用另一个可以接受任意位数的泛型类(比如整数
可以容纳任何4位宽的内容,而不管源“实际”是什么类型)。例如,是否可以假设函数返回长度为16的字节数组(因为sizeof(MyStruct)
Is 16)?在实现的容器类中包装固定大小的数组怎么样?我也找不到这样做的例子Clojure实际上嵌入了的分叉版本,用于生成和加载JVM字节码 在这个库的基础上,我构建了一个小型DSL(不完整,可能已经损坏),它允许使用类似Java的语法创建任意Java类。目前,它只是一个函数,只支持扩展类、实现接口、添加调用超类构造函数的构造函数和字段 以下是使用此DSL解决上述问题的方法:
(def-class MyStruct :extends com.sun.jna.Structure
:implements [com.sun.jna.Structure$ByValue]
; Declare all the constructors, which just forward their
; arguments to the constructors of com.sun.jna.Structure
(com.sun.jna.Structure [])
(com.sun.jna.Structure [com.sun.jna.TypeMapper])
(com.sun.jna.Structure [Integer])
(com.sun.jna.Structure [Integer com.sun.jna.TypeMapper])
(com.sun.jna.Structure [com.sun.jna.Pointer])
(com.sun.jna.Structure [com.sun.jna.Pointer Integer])
(com.sun.jna.Structure [com.sun.jna.Pointer Integer com.sun.jna.TypeMapper])
; Declare the fields of the struct
^Integer foo
^Integer bar
^Double baz)
现在,我可以做以下工作:
(defn createStruct [& args]
(let [fn- (com.sun.jna.Function/getFunction "some-lib" "createStruct")]
(.invoke fn- MyStruct (to-array args))))
(defn addStruct [& args]
(let [fn- (com.sun.jna.Function/getFunction "some-lib" "addStruct")]
(.invoke fn- Double (to-array args))))
(addStruct (createStruct))
; => 10.14
JNA以
com.sun.JNA.memory
的形式提供块内存分配。从那里,您可以使用Pointer.getXXX()
方法从内存中的任意偏移量提取任意类型。@technomage使用memory
作为createStruct
的返回类型,返回一个指针,其地址为0x000000040000003
。换句话说,它将结构的原始内存值视为指针。当取消引用(通过任何read
或getXXX
方法)时,它会使VM崩溃。问题是struct
参数和返回值语义因编译器而异,本地代码实际使用结构
布局来确定如何正确设置堆栈。我建议您可以使用内存
将任意内存块分配给现有的结构.ByValue
实例。您最好询问clojure人员。JNA需要实际参数和库函数映射签名上的Structure.ByValue
标记,以便将结构按值传递给本机代码。如果您确实设法绕过定义公共字段,则需要覆盖结构的(受包保护的)getTypeInfo()
,它返回您的结构(它本身就是一个结构)的FFI类型信息。