gpt4 book ai didi

Java VarHandle 到带有 java.lang.foreign API 的 C 字符串

转载 作者:行者123 更新时间:2023-12-02 15:45:46 28 4
gpt4 key购买 nike

我想使用 panama 项目中的外部函数接口(interface)从 Java19 访问 C 库。 C 接口(interface)非常简单:

typedef struct {
int len;
char name[100];
} ent;

ent* foo();

调用时,函数 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")
);

为了便于访问,我想使用 VarHandle:

private static final VarHandle VH_ENT_LEN = ENT_LAYOUT.varHandle(groupElement("len"));

以后

int len = (int)VH_ENT_LEN.get(segment);
String name = segment.asSlice(ENT_LAYOUT.byteOffset(groupElement("name")), len).getUtf8String(0);

还是有点乱。

我天真的期望是,解决方案应该是这样的:

private static final VarHandle VH_ENT_NAME = ENT_LAYOUT.varHandle(groupElement("name"), sequenceElement());

byte[] nameRaw = (byte[])VH_ENT_NAME.get(segment);

但是我得到:

java.lang.RuntimeException: java.lang.invoke.WrongMethodTypeException:
cannot convert MethodHandle(VarHandle,MemorySegment,long)byte to (VarHandle,MemorySegment)byte[]

所以,问题是:是否有一个优雅的解决方案来从 java 外部 API 访问数组,或者我们应该坚持混合使用 VarHandleslice

最佳答案

VarHandle 从根本上说,仅用于访问适合原始类型的内存,而 char[100] 不适合原始类型。

当你做的时候你会得到什么:

ENT_LAYOUT.varHandle(groupElement("name"), sequenceElement());

是一个 VarHandle,它从数组中选择一个 byte,动态提供索引:

long index = 42; // select element 42
byte nameByte = (byte) VH_ENT_NAME.get(segment, index);

should stick to mix of VarHandle and slice

是的,需要 slice 来访问任何对于基元来说太大的东西。它与在 C 中执行此操作本质上相同:

ent* x = foo();
char* name = x->name;

您也可以使用 MemoryLayout::sliceHandle 来获取嵌入偏移量计算的 MethodHandle:

MethodHandle MH_ENT_NAME = ENT_LAYOUT.sliceHandle(groupElement("name"));

方法句柄也可以进一步组合(就像 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);

不过,定义一个执行上述操作的 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);
}
}

关于Java VarHandle 到带有 java.lang.foreign API 的 C 字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74314769/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com