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

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,它从数组中 Select 一个byte,其索引是动态提供的:

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

应该坚持VarHandleslice的混合

是的,需要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相关问答推荐

ActivityCompat.请求收件箱自动拒绝权限

如何用javac编译Java类,即使对像java.lang.*这样的基本类也没有任何依赖关系?

我应该避免在Android中创建类并在运行时编译它们吗?

SQlite for Android无法使用json_group_array/json_object

Java 8中的多个字段和计数

springboot start loge change

如何判断一个矩阵是否为有框矩阵?

在Java Swing Paint应用程序中捕获快速鼠标移动时遇到困难

计算两个浮点数之间的距离是否对称?

具有阻塞方法的开源库是否应该为执行提供异步选项?

解析方法";javax/imageio/metadata/IIOMetadata.getAsTree(Ljava/lang/String;)Lorg/w3c/dom/Node时加载约束冲突

Oj算法 MatrixR032从字符串、归一化和余弦相似度计算创建

如何生成指定范围内的11位序列号?

Java KeyListener不工作或被添加

Java泛型方法重载

原始和参数化之间的差异调用orElseGet时可选(供应商)

获取月份';s在java中非UTC时区的开始时间和结束时间

如何在Java上为循环数组从synchronized迁移到ReentrantLock

java构造函数中的冻结操作何时发生?

为什么child-pom会创建一个新版本