如何使用GraalVM从C ++调用具有非原始类型作为参数的Java入口点方法

问题描述

我正在尝试使用graalvm创建Java代码的共享库(带有标头和lib文件的dll)。

会有一个带有2个String类型参数的Java方法,我将从c ++调用

我正在使用Maven项目,无法使用Java代码创建dll,而是使用graalvm将Java代码转换为dll。

我的Java代码如下:

package demo;

import org.graalvm.nativeimage.IsolateThread;
import org.graalvm.nativeimage.c.function.CEntryPoint;

public class MyClass{
    
    @CEntryPoint (name = "myFunc")
    public static byte[] myfunc(IsolateThread thread,String x,String y) {

        // logic goes here

        byte[] arr = "byte array will contain actual bytes".getBytes();
        
        return arr;
    }

但是当我尝试将代码构建到dll中时,出现此错误

错误:入口点方法参数类型仅限于基本类型,单词类型和枚举(@CEnum):demo.MyClass.myFunc(IsolateThread,String,String)

我进行了搜索,但没有找到适合该问题的解决方案。 有人可以告诉我如何使用非原始数据类型从c ++调用Java方法 任何形式的建议都会对您有所帮助

解决方法

要在GraalVM中从C或C ++成功运行Java方法,必须满足一些特定的先决条件。 在设计将用@CEntryPoint注释的Java入口点方法时,也应考虑这些因素。 in the CEntryPoint documentation中提到了以下前提条件。

  1. Java入口点方法仅允许包含原始Java类型,单词值和枚举。同样,为了实际使用Enumenum class必须具有CEnum注释。In the CEntryPoint documentation
  2. Java入口点方法应该是静态的。
  3. Java入口点方法应捕获所有异常,因为它不应引发任何异常。如果未捕获到异常,则将其打印,然后终止该过程。但是@CEntryPoint文档明确提到要捕获Java入口点方法内部的所有异常。
  4. 需要IsolateThread参数。更精确地

必须将执行上下文作为参数传递,并且可以是 特定于当前线程的IsolateThread或Isolate 用于连接当前线程的隔离。这些指针 可以通过CurrentIsolate的方法获得。当还有更多 而不是这些类型的一个参数,则其中一个参数必须 用CEntryPoint.IsolateThreadContext注释IsolateThread, 或CEntryPoint.IsolateContext进行隔离。

您的问题中的示例引发此错误,因为myFunc方法签名包括对象,例如String参数。即xy。根据上面的前提 1 ,不允许这样做。这就是错误描述要说的。

解决方案是使用提供的功能在Java类型和C类型之间进行转换。在这种情况下,为了在CJava之间传递文本,我们可以使用CCharPointer。由于文本在CJava中的建模方式不同,因此必须将Java String转换为C *char,反之亦然。

创建并返回Java字符串

下面有一个示例,当byte[]表示文本时即可使用。

//These are the imports needed
import org.graalvm.nativeimage.c.type.CCharPointer;
import org.graalvm.nativeimage.c.type.CTypeConversion;

@CEntryPoint(name = "myFunc")
  public static CCharPointer myFunc(IsolateThread thread,CCharPointer x,CCharPointer y) {
        //Convert C *char to Java String
        final String xString= CTypeConversion.toJavaString(x);
        final String yString= CTypeConversion.toJavaString(y);

        //logic goes here

        //Convert Java String to C *char
        try(final CTypeConversion.CCharPointerHolder holder=CTypeConversion.toCString("Hello from Java")){
        
                final CCharPointer result=holder.get();
                return result;
         }
        
  }

使用并返回在C中分配的数组

您还可以遵循C样式并将C中的数组作为参数传递,然后使用该数组在Java中写入结果字节值。方法CCharPointer.write(int,byte)可以将Java byte的值写入*charchar[]中的特定数组索引。如果需要,也可以返回字节数组。

@CEntryPoint(name = "myFunc2")
  public static CCharPointer myFunc2(IsolateThread thread,CCharPointer y,CCharPointer resultArray,int resultArrayLength) {

        //Convert C *char to Java String
        final String xString= CTypeConversion.toJavaString(x);
        final String yString= CTypeConversion.toJavaString(y);

        //logic goes here

        //Fill in the result array
        final byte sampleByteValue=7;
        for(int index =0; index<resultArrayLength; index++){
        resultArray.write(index,sampleByteValue);
        }
        return resultArray;
  }

使用Java NIO ByteBuffer

对于较大的字节数组,您可以检查CTypeConversion,以创建具有特定容量的Java NIO ByteBuffer,该容量引用本地内存。请注意

调用方负责确保可以安全存储 在使用ByteBuffer时访问,并用于释放内存 之后。