【问题标题】:How to call a Java entrypoint method with non-primitive types as parameters from C++ using GraalVM如何使用 GraalVM 从 C++ 调用具有非原始类型作为参数的 Java 入口点方法
【发布时间】:2020-09-25 08:45:45
【问题描述】:

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

将有一个带有 2 个字符串类型参数的 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 方法 任何形式的建议都会有很大的帮助,在此先感谢

【问题讨论】:

    标签: java c++ visual-c++ graalvm graalvm-native-image


    【解决方案1】:

    Java 方法需要满足特定的先决条件才能在 GraalVM 中从 C 或 C++ 成功运行。 在设计将使用@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 的方法获得。当有更多 不是这些类型的一个参数,必须是其中一个参数 为 IsolateThread 使用 CEntryPoint.IsolateThreadContext 进行注释, 或 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 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 时访问,并释放内存 之后。

    【讨论】:

    • 首先非常感谢,java和c++之间字符串类型的转换我懂了。在我的情况下, byte[] 将不包含文本,它将仅包含字节,它也适用吗?
    • 不客气。是的,我会更新我的答案以包含一个示例。
    • 我的字节数组会很大,我可以一次写入整个字节数组还是必须只在特定索引处写入?
    • 使用 NIO 字节缓冲区本身就是一个主题,也许是一个不同的问题,因为 NIO 是独立于 GraalVM 的东西。你可以在一个新问题中尝试一些东西,如果可能的话我可以提供帮助。例如,请参见javarticles.com/2018/04/java-nio-bytebuffer.html 传递对象是不可能的,正如我的回答和 javadoc 中的前提条件 1 中所述,您可以获得的最接近的是使用独立格式,例如JSON 和根据需要编组/解组。
    • 您好,您的回答再次奏效,我的编码类型有问题。实际上,我需要一个 base64 编码的字符串,但我不知道 CTypeConversion 使用什么编码,因为我无法使用 base64 解码器将它解码为字节数组,你能帮我吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-05
    • 2014-01-10
    • 1970-01-01
    • 2019-07-21
    • 2014-08-26
    相关资源
    最近更新 更多