【问题标题】:Passing 2d array from C++ to Java and back to C++将二维数组从 C++ 传递到 Java 并返回到 C++
【发布时间】:2015-05-30 08:19:31
【问题描述】:

如何将二维数组从 C++ 传递到 Java 并使用 JNI 返回到 C++?

Sample2.java

public static int[][] intArrayMethod(int[][] n){
    for (int i = 0; i < 10; i++){
        for (int j = 0; j < 10; j++){
            n[i][j] = 2;
        }
    }
    return n;
}

CppSample.cpp

int main(){
    JavaVMOption options[1];
    JNIEnv *env;
    JavaVM *jvm;
    JavaVMInitArgs vm_args;
    long status;
    jclass cls;
    jmethodID mid;

    options[0].optionString = "-Djava.class.path=.";
    memset(&vm_args, 0, sizeof(vm_args));
    vm_args.version = JNI_VERSION_1_2;
    vm_args.nOptions = 1;
    vm_args.options = options;
    status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);

    jint cells[10][10]; // my 2d array
    for (int i = 0; i < 10; i++){
        for (int j = 0; j < 10; j++){
            cells[i][j] = 1;
        }
    }

    if (status != JNI_ERR){
        cls = env->FindClass("Sample2");
        if (cls != 0){
            mid = env->GetStaticMethodID(cls, "intArrayMethod", "(I)I");
            if (mid != 0){
                cells = env->CallStaticIntMethod(cls, mid, cells);
                for (int i = 0; i < 10; i++){
                    for (int j = 0; j < 10; j++){
                        printf("%d", cells[i][j]);
                    }
                }
            }
        }
        jvm->DestroyJavaVM();
        return 0;
    }
    else{
        return 1;
    }
}

我不确定在 c++ 和 java 之间传递二维数组的正确方法是什么。

希望您能指导我,谢谢!

【问题讨论】:

    标签: java c++ arrays multidimensional-array java-native-interface


    【解决方案1】:

    这并不困难,而是乏味。一般的问题是Java中的数组与C++中的数组完全不同,并且(在某种程度上)更类似于不可调整大小的向量。无法将 C++ 数组直接传递给 Java1,因为 Java 不知道如何处理它。所以我们需要转换函数,例如:

    将二维数组从 C++ 转换为 Java

    // The template bit here is just to pick the array dimensions from the array
    // itself; you could also pass in a pointer and the dimensions. 
    template<std::size_t OuterDim, std::size_t InnerDim>
    jobjectArray to_java(JNIEnv *env, int (&arr)[OuterDim][InnerDim]) {
      // We allocate the int array first
      jintArray    inner = env->NewIntArray   (InnerDim);
      // to have an easy way to get its class when building the outer array
      jobjectArray outer = env->NewObjectArray(OuterDim, env->GetObjectClass(inner), 0);
    
      // Buffer to bring the integers in a format that JNI understands (jint
      // instead of int). This step is unnecessary if jint is just a typedef for
      // int, but on OP's platform this appears to not be the case.
      std::vector<jint> buffer;
    
      for(std::size_t i = 0; i < OuterDim; ++i) {
        // Put the data into the buffer, converting them to jint in the process
        buffer.assign(arr[i], arr[i] + InnerDim);
    
        // then fill that array with data from the input
        env->SetIntArrayRegion(inner, 0, InnerDim, &buffer[0]);
        // and put it into the outer array
        env->SetObjectArrayElement(outer, i, inner);
    
        if(i + 1 != OuterDim) {
          // if required, allocate a new inner array for the next iteration.
          inner = env->NewIntArray(InnerDim);
        }
      }
    
      return outer;
    }
    

    从 Java 到 C++

    // Note that this function does not return an array but a vector of vectors
    // because 2-dimensional Java arrays need not be rectangular and have
    // dynamic size. It is not generally practical to map them to C++ arrays.
    std::vector<std::vector<int> > from_java(JNIEnv *env, jobjectArray arr) {
      // always on the lookout for null pointers. Everything we get from Java
      // can be null.
      jsize OuterDim = arr ? env->GetArrayLength(arr) : 0;
      std::vector<std::vector<int> > result(OuterDim);
    
      for(jsize i = 0; i < OuterDim; ++i) {
        jintArray inner = static_cast<jintArray>(env->GetObjectArrayElement(arr, i));
    
        // again: null pointer check
        if(inner) {
          // Get the inner array length here. It needn't be the same for all
          // inner arrays.
          jsize InnerDim = env->GetArrayLength(inner);
          result[i].resize(InnerDim);
    
          jint *data = env->GetIntArrayElements(inner, 0);
          std::copy(data, data + InnerDim, result[i].begin());
          env->ReleaseIntArrayElements(inner, data, 0);
        }
      }
    
      return result;
    }
    

    如您所见,它们非常简单;真的只需要知道要调用哪些 JNI 函数(或者,实际上,在哪里可以找到 the documentation)。

    调用 Java 函数

    至于调用 Java 函数,你几乎是对的。有两件事需要改变:

    mid = env->GetStaticMethodID(cls, "intArrayMethod", "(I)I");
    

    如果 intArrayMethodint intArrayMethod(int) 将有效。事实上,你需要

    mid = env->GetStaticMethodID(cls, "intArrayMethod", "([[I)[[I");
    

    在这些签名字符串中,I 代表int[I 代表int[][[I 代表int[][]。一般来说,[type 代表type[]

    这里:

    cells = env->CallStaticIntMethod(cls, mid, cells);
    

    两件事:

    1. 正如一开始提到的,cells 不能按原样传递,因为 Java 不明白它是什么;需要先转换成Java数组(比如上面的转换函数),然后
    2. intArrayMethod 不是 IntMethod;它不返回int,而是返回一个复杂的数据类型。正确的调用必须使用 CallStaticObjectMethod 并将 jobject 转换为实际有用的 JNI 类型。

    正确的做法是:

    jobjectArray java_cells = static_cast<jobjectArray>(env->CallStaticObjectMethod(cls, mid, to_java(env, cells)));
    

    这为您提供了一个jobjectArray,您可以使用其他转换函数将其转换为向量的 C++ 向量:

    std::vector<std::vector<int> > cpp_cells = from_java(env, java_cells);
    

    这可以像任何向量一样使用。

    我可以提到一些样式问题(例如在 Java 代码中依赖数组始终为 10x10,或者在有更好(阅读:类型安全)C++ 替代方案的地方使用或 C 函数——我在看着你, printf),但它们似乎不会在此特定程序中引发问题。

    1 除了通过指针将其传递回本机代码

    【讨论】:

    • 您好,感谢您的明确解释。但是,我在构建代码时遇到了错误。 "JNIEnv_::SetIntArrayRegion(jintArray,jsize,jsize,const jint *)' : 无法将参数 4 从 'int [10]' 转换为 'const jint *'”
    • 啊。我想如果intjint 不同,就会发生这种情况,尽管我认为在这种情况下不存在平台并且存在Java。那么,在将它交给 Java 之前,我们需要制作一个数组的副本。给我一分钟。
    • 我做了一个简单的修复程序,应该可以解决问题。我仍然有点惊讶这是必要的。您介意告诉我您使用的是什么平台吗?
    • 我目前正在使用 VS 2013 开发一个 win32 应用程序,非常感谢。你是救生员!我一整天都在想办法让它发挥作用。
    • 啊。我只是查看了 JNI 标头;在 Windows 上,它使用typedef long jint;,编译器不能隐式地从int[] 转换为long*。由于该平台上的sizeof(int) == sizeof(long),它可以只使用reinterpret_cast 指针并将额外的副本保存到向量中,但是......除非性能考虑使得该步骤成为必要,否则我会避免这种恶作剧。这是制造调试噩梦的东西。
    猜你喜欢
    • 2015-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-19
    • 2018-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多