发布
社区首页 >问答首页 >用VarHandle API将java.lang.foreign转换为C字符串

用VarHandle API将java.lang.foreign转换为C字符串
EN

Stack Overflow用户
提问于 2022-11-04 09:27:47
回答 1查看 55关注 0票数 2

我想使用项目巴拿马的外部函数接口从Java19访问C库。C接口非常简单:

代码语言:javascript
代码运行次数:0
复制
typedef struct {
  int len;
  char name[100];
} ent;

ent* foo();

调用时,函数foo返回指向struct ent的指针,其中len告诉字符串name的大小。

相应的Java端是:

代码语言:javascript
代码运行次数:0
复制
private static final MemoryLayout ENT_LAYOUT = MemoryLayout.structLayout(
        JAVA_INT.withName("len"),
        MemoryLayout.sequenceLayout(100, ValueLayout.JAVA_BYTE).withName("name")
);

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

代码语言:javascript
代码运行次数:0
复制
private static final VarHandle VH_ENT_LEN = ENT_LAYOUT.varHandle(groupElement("len"));

后来

代码语言:javascript
代码运行次数:0
复制
int len = (int)VH_ENT_LEN.get(segment);
String name = segment.asSlice(ENT_LAYOUT.byteOffset(groupElement("name")), len).getUtf8String(0);

但还是有点乱。

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

代码语言:javascript
代码运行次数:0
复制
private static final VarHandle VH_ENT_NAME = ENT_LAYOUT.varHandle(groupElement("name"), sequenceElement());

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

不过,我明白了:

代码语言:javascript
代码运行次数:0
复制
java.lang.RuntimeException: java.lang.invoke.WrongMethodTypeException:
   cannot convert MethodHandle(VarHandle,MemorySegment,long)byte to (VarHandle,MemorySegment)byte[]

因此,问题是:是否有一个优雅的解决方案来访问来自java的数组,或者我们应该坚持VarHandleslice的混合。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-11-04 13:41:28

VarHandle在根上只用于访问可以适应基本类型的内存,而char[100]不适合于原语。

你在做的时候得到了什么:

代码语言:javascript
代码运行次数:0
复制
ENT_LAYOUT.varHandle(groupElement("name"), sequenceElement());

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

代码语言:javascript
代码运行次数:0
复制
long index = 42; // select element 42
byte nameByte = (byte) VH_ENT_NAME.get(segment, index);

应该坚持VarHandleslice的混合

是的,对于原语来说,访问任何太大的东西都需要slice。这在本质上与在C中这样做是一样的:

代码语言:javascript
代码运行次数:0
复制
ent* x = foo();
char* name = x->name;

您还可以使用MemoryLayout::sliceHandle获得嵌入偏移计算的MethodHandle

代码语言:javascript
代码运行次数:0
复制
MethodHandle MH_ENT_NAME = ENT_LAYOUT.sliceHandle(groupElement("name"));

方法句柄也可以进一步组合(就像varhandles一样),以创建一个直接从段中获取字符串的句柄:

代码语言:javascript
代码运行次数:0
复制
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助手方法通常更简单:

代码语言:javascript
代码运行次数:0
复制
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);
    }
}
票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74314769

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档