kof*_*ann 2 java java-native-interface project-panama java-18
我想使用巴拿马项目中的外部函数接口来访问 Java19 中的 C 库。C 接口非常简单:
typedef struct {
int len;
char name[100];
} ent;
ent* foo();
Run Code Online (Sandbox Code Playgroud)
调用时,函数 foo 返回指向 的指针struct ent,其中len指示字符串的大小name。
对应的Java端是:
private static final MemoryLayout ENT_LAYOUT = MemoryLayout.structLayout(
JAVA_INT.withName("len"),
MemoryLayout.sequenceLayout(100, ValueLayout.JAVA_BYTE).withName("name")
);
Run Code Online (Sandbox Code Playgroud)
为了方便访问,我想使用VarHandle:
private static final VarHandle VH_ENT_LEN = ENT_LAYOUT.varHandle(groupElement("len"));
Run Code Online (Sandbox Code Playgroud)
以及后来
int len = (int)VH_ENT_LEN.get(segment);
String name = segment.asSlice(ENT_LAYOUT.byteOffset(groupElement("name")), len).getUtf8String(0);
Run Code Online (Sandbox Code Playgroud)
这仍然有点混乱。
我天真的期望解决方案应该是这样的:
private static final VarHandle VH_ENT_NAME = ENT_LAYOUT.varHandle(groupElement("name"), sequenceElement());
byte[] nameRaw = (byte[])VH_ENT_NAME.get(segment);
Run Code Online (Sandbox Code Playgroud)
但是我得到:
java.lang.RuntimeException: java.lang.invoke.WrongMethodTypeException:
cannot convert MethodHandle(VarHandle,MemorySegment,long)byte to (VarHandle,MemorySegment)byte[]
Run Code Online (Sandbox Code Playgroud)
所以,问题是:是否有一个优雅的解决方案来从 java 外部 API 访问数组,或者我们应该坚持混合使用VarHandle和slice。
VarHandle从根本上讲,它们仅用于访问适合原始类型的内存,而不char[100]适合原始类型。
做的时候你会得到什么:
ENT_LAYOUT.varHandle(groupElement("name"), sequenceElement());
Run Code Online (Sandbox Code Playgroud)
是从数组中VarHandle选择一个的byte,其索引是动态提供的:
long index = 42; // select element 42
byte nameByte = (byte) VH_ENT_NAME.get(segment, index);
Run Code Online (Sandbox Code Playgroud)
应该坚持
VarHandle和的混合slice
是的,slice需要访问任何对于基元来说太大的东西。它本质上与在 C 中执行此操作相同:
ent* x = foo();
char* name = x->name;
Run Code Online (Sandbox Code Playgroud)
您也可以使用MemoryLayout::sliceHandle来获得MethodHandle嵌入偏移计算的 a :
MethodHandle MH_ENT_NAME = ENT_LAYOUT.sliceHandle(groupElement("name"));
Run Code Online (Sandbox Code Playgroud)
方法句柄还可以进一步组合(就像 varhandles 一样),以创建直接从段中获取字符串的方法句柄:
MethodHandle MH_getUtf8String = MethodHandles.lookup().findVirtual(MemorySegment.class, "getUtf8String", MethodType.methodType(String.class, long.class));
MethodHandle mh = MethodHandles.insertArguments(MH_getUtf8String, 1, 0); // always access string at offset 0
mh = MethodHandles.filterArguments(result, 0, MH_ENT_NAME);
String name = (String) mh.invokeExact(segment);
Run Code Online (Sandbox Code Playgroud)
static不过,定义一个执行上述操作的辅助方法通常更简单:
public static String getName(MemorySegment segment) {
try {
MemorySegment nameSegment = (MemorySegment) MH_ENT_NAME.invokeExact(segment);
return nameSegment.getUtf8String(0);
} catch(Throwable t) {
throw new RuntimeException(t);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
383 次 |
| 最近记录: |