2

我正在尝试使用 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 方法任何类型的建议都会有很大的帮助,在此先感谢

4

1 回答 1

3

为了在 GraalVM 中从 C 或 C++ 成功运行,Java 方法需要满足特定的先决条件。在设计将要使用 注释的 Java 入口点方法时应考虑这些前提条件@CEntryPointCEntryPoint 文档中提到了以下先决条件。

  1. Java 入口点方法只允许包含原始 Java 类型、字值和枚举。另外为了实际使用Enum必须enum class有一个CEnum注解。在 CEntryPoint 文档中。
  2. Java 入口点方法应该是静态的。
  3. Java 入口点方法应该捕获所有异常,因为它不应该抛出任何异常。如果未捕获到异常,则打印该异常,然后终止该过程。但是,@CEntryPoint文档明确提到要捕获 java 入口点方法中的所有异常。
  4. 需要一个IsolateThread参数。更确切地说

执行上下文必须作为参数传递,可以是特定于当前线程的 IsolateThread,也可以是附加当前线程的隔离的 Isolate。这些指针可以通过 CurrentIsolate 的方法获得。当这些类型的参数不止一个时,对于 IsolateThread,必须使用 CEntryPoint.IsolateThreadContext 或对 Isolate 使用 CEntryPoint.IsolateContext 注释其中一个参数。

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

解决方案是使用提供的功能在 Java 类型和 C 类型之间进行转换。在这种情况下,为了在和之间传递文本CJava我们可以使用CCharPointer. 由于文本在C和中Java的建模方式不同,Java String因此必须将 a 转换为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)可以将值写入orJava byte中的特定数组索引。如果需要,也可以返回字节数组。*charchar[]

@CEntryPoint(name = "myFunc2")
  public static CCharPointer myFunc2(IsolateThread thread
         , CCharPointer x, 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 时可以安全地访问内存,并在之后释放内存。

于 2020-09-27T15:05:09.323 回答