java - 如何从 C++ 发送二维数组并在 Java 中编辑值并通过 JNI 将二维数组发送回 C++

标签 java c++ arrays java-native-interface

根据主题,我有一个名为 cells 的二维数组,它包含一些 C++ 中的值。

我想将这个二维数组发送到我的 Java 文件中,该文件将替换其中的一些值。有没有一种方法可以在 Java 中从 C++“接收”数组?

目前,我可以使用 C++ 从 Java 打印我的内容。我的源代码如下:

C++ 源代码:

//JNI
#include <jni.h>
#include <stdio.h>
#include <string.h>
#include <vector>

//jvm
JavaVMOption options[1];
JNIEnv *env;
JavaVM *jvm;
JavaVMInitArgs vm_args;
long status;
jclass cls;
jmethodID mid;

typedef std::vector<std::vector<int>> IntMatrix;
IntMatrix cells;

//fn header
template<std::size_t OuterDim, std::size_t InnerDim>
jobjectArray to_java(JNIEnv *env, int(&arr)[OuterDim][InnerDim]);
std::vector<std::vector<int> > from_java(JNIEnv *env, jobjectArray arr);
jobjectArray v2jObs(IntMatrix v2D);




int main(){
    cells = { { 0, 0, 0, 1, 0, 0, 0, 0, 0, 1 }, { 0, 1, 1, 1, 1, 1, 0, 1, 0, 1 }, { 0, 1, 0, 0, 0, 1, 0, 1, 0, 1 }, { 0, 1, 1, 1, 0, 1, 1, 1, 0, 1 }, { 0, 0, 0, 1, 0, 1, 0, 0, 0, 1 }, { 0, 1, 1, 1, 0, 1, 0, 1, 0, 1 }, { 0, 1, 0, 0, 0, 1, 0, 1, 0, 1 }, { 0, 1, 1, 1, 0, 1, 1, 1, 0, 1 }, { 0, 0, 0, 1, 0, 0, 0, 0, 0, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } };

    options[0].optionString = "-Djava.class.path=C:\\Users\\Downloads\\ConsoleApplication2\\ConsoleApplication2";
    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);

    //int 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){
                jobjectArray java_cells = static_cast<jobjectArray>(env->CallStaticObjectMethod(cls, mid, v2jObs(cells)));
                std::vector<std::vector<int> > cpp_cells = from_java(env, java_cells);
                for (int i = 0; i < 10; i++){
                    for (int j = 0; j < 10; j++){
                        printf("%d", cpp_cells[i][j]);
                    }
                }
            }
        }
        jvm->DestroyJavaVM();
        return 0;
    }
    else{
        return 1;
    }


}


// 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;
}


// 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;
}


jobjectArray v2jObs(IntMatrix v2D){
    int y = v2D.size();
    int x = v2D[0].size();
    jint **arr2D = new jint*[y];

    jintArray inner = env->NewIntArray(x);
    jobjectArray outer = env->NewObjectArray(y, env->GetObjectClass(inner), 0);

    for (unsigned i = 0; (i < y); i++)
    {
        arr2D[i] = new jint[x];
        for (unsigned j = 0; (j < x); j++)
        {
            arr2D[i][j] = v2D[i][j];
        }
        env->SetIntArrayRegion(inner, 0, x, arr2D[i]);
        env->SetObjectArrayElement(outer, i, inner);
    }
    return outer;

}

IntMatrix jObs2v(jobjectArray arr2D){
    int y = env->GetArrayLength(arr2D);
    //int x = env->GetObjectLength(env->GetObjectArrayElement(arr2D, 0));
    IntMatrix result(y);
    return result;
}

Java:

public class Sample2{
    public static void main(String []args){
        //
    }

    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;
    }
}

我是否必须在我的 Java 程序中包含一些代码才能从 C++ 中“接收”cell 二维数组值?我整晚都在搜索,但无法正常工作。请帮忙!!!

最佳答案

我将类重命名为 SampleSO,将 Array 设为 3x3,并使 intArrayMethod 方法将每个单元格的值加倍。为简单起见,我没有使用您的 v2jObs 函数和您的 IntMatrix 类型。

我复制/粘贴了两个 C++ 函数 to_javafrom_java严格未修改(下面省略)

Java 类:

public class SampleSO{
    public static void main(String []args){}

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

C++ 源代码(没有 to_javafrom_java)

#include <jni.h>
#include <cstring>
#include <vector>

int wmain( int argc, wchar_t * argv[] ) {

    JavaVMOption options[1];
    options[0].optionString = "-Djava.class.path=C:/Users/manuel/Documents/Visual Studio 2010/Projects/JavaArray";

    JavaVMInitArgs vm_args;
    memset( &vm_args, 0, sizeof( vm_args ) );

    vm_args.version = JNI_VERSION_1_2;
    vm_args.nOptions = 1;
    vm_args.options = options;

    JavaVM *jvm;
    JNIEnv *env;
    jint status = JNI_CreateJavaVM( &jvm, (void**)&env, &vm_args );
    printf( "JNI_CreateJavaVM said %d\n", status );
    if ( status != JNI_ERR ) {
        jclass cls = env->FindClass( "SampleSO" );
        if ( cls != nullptr ) {
            printf( "Class Found\n" );
            jmethodID mid = env->GetStaticMethodID( cls, "intArrayMethod", "([[I)[[I");
            if ( mid != nullptr ) {
                printf( "Method Found\n" );
                int Array[3][3] = { { 1, 1, 1 }, { 2, 2, 2 }, { 3, 3, 3 }, };
                jobjectArray java_cells = static_cast<jobjectArray>( env->CallStaticObjectMethod( cls, mid, to_java( env, Array ) ) );
                printf( "Method Called\n" );
                std::vector<std::vector<int> > Result = from_java( env, java_cells );
                printf( "Dumping Result:\n" );
                for ( size_t RowIndex = 0; RowIndex < Result.size(); ++RowIndex ) {
                    for ( size_t ColIndex = 0; ColIndex < Result[ RowIndex ].size(); ++ColIndex ) {
                        printf( "%d ", Result[ RowIndex ][ ColIndex ] );
                    } // end for
                    printf( "\n" );
                } // end for
            } // endif
        } // endif
    } // endif

    return 0;

} // wmain

结果: Result from executing in VS2010, Java 1.6 x86

请注意:

  • Windows 7 64 位、Oracle Java x86 1.6、Visual Studio 2010
  • 没有内存清理
  • 您可以在 C++ 中使用 ExceptionCheckExceptionOccurred,然后在 JNI 调用之后使用 ExceptionClear

关于java - 如何从 C++ 发送二维数组并在 Java 中编辑值并通过 JNI 将二维数组发送回 C++,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29313961/

相关文章:

c++ - 如何使程序声明可变数量的不确定大小的数组

java - 在 Java 中实现最佳匹配搜索

java - 从 BigQuery 读取数据并将其写入云存储上的 avro 文件格式

java - 寻找 vector 之间的符号角

c++ - Oracle Pro*C 预编译器错误 PCC-S-02201

c++ - 如何使用boost检查文件是否已经打开

c++ - 如何在 x86 程序集中移动两个 float 相乘的结果?

Jquery 数组查找并替换部分匹配的条目

java - 并发代码中赋值运算符的返回值

javascript - 获取另一个数组下的数组名