Question

I'm trying to run a native method in java and then receiving a 2D double array that I can use in java. I've successfully manipulated the data in C but now I need to send it back to java. How would I convert a 2D double array in C to a 2D double array in java?

I've tried this simple code I came up with but it unfortunately doesn't work:

jobjectArray convertToArray(JNIEnv *env, double **data, int length1D,
            int *length2D) {
        jsize outerSize = (jsize) length1D; /* EDIT thanks to John Bickers */
        jclass class = (*env)->FindClass(env, "[D"); /* EDIT thanks to Nam San */
        jobjectArray outer = (*env)->NewObjectArray(env, outerSize, class, 0);
        jsize i;
        for (i = 0; i < outerSize; i++) {
            jsize innerSize = (jsize) length2D[i]; /* EDIT thanks to John Bickers */
            jdoubleArray inner = (*env)->NewDoubleArray(env, innerSize);
            (*env)->SetDoubleArrayRegion(env, inner, 0, innerSize, data[i]);
            (*env)->SetObjectArrayElement(env, outer, i, inner);
            (*env)->DeleteLocalRef(env, inner); /* EDIT thanks to Tom Blodget */
        }
        return outer;
    }

And this is the macro called 'getLength':

#define getLength(x)  (sizeof(x) / sizeof(x[0]))

When I run this it creates a mindump (which I can't understand):

I'm new to both the JNI and C programming, so please explain rather detailed what exactly is wrong and how I would fix it.

Thanks in advance!

UPDATE I found out that the error is not about the code itself, more about the array I'm trying to convert. It turns out that one element in the array contains alot more elements than the other ones. I'm trying to read from a level-4 matlab file with help of the library matio. Maybe the error is located there? I'll put everything below:

#include <jni.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <matio.h>
#include "ReadMatFile.h"

//macros
#define getLength(x)  (sizeof(x) / sizeof(x[0]))

//variables
double *dataMatrix[4];
int innerSize[getLength(dataMatrix)];

//functions
jobjectArray convertToArray(JNIEnv *env, double **data, int length1D,
        int *length2D);

JNIEXPORT jobjectArray JNICALL Java_ReadMatFile_readMatFile(JNIEnv *env,
        jobject object, jstring str) {
    const char *fileName = (*env)->GetStringUTFChars(env, str, 0);
    mat_t *matfp;
    matvar_t *matvar;
    printf("%s\n", fileName);
    matfp = Mat_Open(fileName, MAT_ACC_RDONLY | MAT_FT_MAT4);
    if ( NULL == matfp) {
        fprintf(stderr, "Error opening MAT file %s\n", fileName);
        return NULL;
    }
    int i = 0;
    while ( NULL != (matvar = Mat_VarReadNext(matfp))) {
        double *data = (double*) (matvar->data);
        dataMatrix[i] = data;
        innerSize[i] = (int) matvar->nbytes / matvar->data_size;
        Mat_VarFree(matvar);
        matvar = NULL;
        i++;
    }
    Mat_Close(matfp);
    (*env)->ReleaseStringUTFChars(env, str, fileName);
    int outerSize = (int) getLength(dataMatrix);
    return convertToArray(env, dataMatrix, outerSize, innerSize);
}

jobjectArray convertToArray(JNIEnv *env, double **data, int length1D,
        int *length2D) {
    jsize outerSize = (jsize) length1D;
    jclass class = (*env)->FindClass(env, "[D");
    jobjectArray outer = (*env)->NewObjectArray(env, outerSize, class, 0);
    jsize i;
    for (i = 0; i < outerSize; i++) {
        jsize innerSize = (jsize) length2D[i];
        jdoubleArray inner = (*env)->NewDoubleArray(env, innerSize);
        (*env)->SetDoubleArrayRegion(env, inner, 0, innerSize, data[i]);
        (*env)->SetObjectArrayElement(env, outer, i, inner);
        (*env)->DeleteLocalRef(env, inner);
    }
    return outer;
}

Another Update

It seems like the error is in the first element of the array. I'm sure about this because if I run the code on another array with less values it works perfectly fine. But if I try to run this code where I'm ONLY returning the first array it creates a minidump:

#include <jni.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <matio.h>
#include "ReadMatFile.h"

//macros
#define getLength(x)  (sizeof(x) / sizeof(x[0]))

//variables
double *dataMatrix[4];
int innerSize[getLength(dataMatrix)];

//functions
jobjectArray convertTo1DArray(JNIEnv *env, double *data, int length);

JNIEXPORT jdoubleArray JNICALL Java_ReadMatFile_readMatFile(JNIEnv *env,
        jobject object, jstring str) {
    const char *fileName = (*env)->GetStringUTFChars(env, str, 0);
    mat_t *matfp;
    matvar_t *matvar;
    matfp = Mat_Open(fileName, MAT_ACC_RDONLY | MAT_FT_MAT4);
    if ( NULL == matfp) {
        fprintf(stderr, "Error opening MAT file %s\n", fileName);
        return NULL;
    }
    int i = 0;
    while ( NULL != (matvar = Mat_VarReadNext(matfp))) {
        double *data = (double*) (matvar->data);
        dataMatrix[i] = data;
        innerSize[i] = (int) matvar->nbytes / matvar->data_size;
        Mat_VarFree(matvar);
        matvar = NULL;
        i++;
    }
    Mat_Close(matfp);
    (*env)->ReleaseStringUTFChars(env, str, fileName);
    return convertTo1DArray(env, dataMatrix[1], innerSize[1]);
}

jobjectArray convertTo1DArray(JNIEnv *env, double *data, int length) {
    jdoubleArray arr = (*env)->NewDoubleArray(env, (jsize) length);
    (*env)->SetDoubleArrayRegion(env, arr, 0, (jsize) length, data);
    (*env)->DeleteLocalRef(env, arr);
    return arr;
}

However, if I run the code on the next element in the array (i.e index 1 instead of index 0) it doesn't create a minidump nor does it fail to return the array. The reason it creates a minidump is most likely because the first array contains 1000004 double values retrieved from a level-4 matlab file. So this question is already solved, thanks to everyone. But unfortunently the problem I described still remains (i.e it still creates the minidump). So thanks everyone, I'll probably post another question concerning the huge array problem..

Was it helpful?

Solution

One big problem is that getLength(data) isn't returning what you expect. Because the function is given a pointer to a pointer to a double, sizeof(x) is the size of a pointer to a pointer, and sizeof(x[0]) is the size of a pointer to a double. The result is most likely 1. Inside the function your code has no way to tell what the dimensions of the data matrix are.

You should probably change the function so that data is just a double *, add two more parameters to give the two array dimensions, innerSize and outerSize, and do some arithmetic to get the index into data when you call SetDoubleArrayRegion().

OTHER TIPS

This is a duplicate of How to return 2D long array with JNI except that you want a 2D double array instead of a 2D long array. For 2D double arrays your FindClass() method needs to find "[D" instead of "java/lang/Double"

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top