Handling Java objects in MEX files

In case you are right now having the same doubts as author of this post, I might have some good news for you. The following snippet of code was supposed to be part of my upcoming Weka toolbox for SPM8. It copies content of a 2-D full real MATLAB array into java.lang.Double[][] of the same dimensions given as the second argument. I ended up using another solution to pass my data around, but nevertheless, I found out how to correctly handle opaque Java objects from a MEX file using JNI. It took some debugging to discover, that the actual jobject handle is stored at (jobject*)(((char*) mxGetData(prhs[n]))+16), where n – index of your Java object argument. It means that you can use mxGetClassName(prhs[n]) to make sure that it is the expected Java type, in this case java.lang.Double[][] and then do any kind of manipulations using JNI. I didn’t discover what other secrets mxGetData(prhs[n]) holds, therefore I cannot tell you for example how to safely create new Java objects, wrap them in an mxArray and return to MATLAB, but I can propose this simple workaround. Pass a java.lang.Object[] array (or even java.util.Vector<Object>) as parameter to your MEX function and put all your result objects in there. That’it. Mystery solved. I’m pretty sure I will have some use for these findings in the future, maybe you will as well. Cheers!

PS. I tested on 64-bit MATLAB R2011b on Windows. It might be different in other versions but probably the address is the same or very near by.

//
// Weka Toolbox for SPM
//
// Copyright (C) 2012, Stanislaw Adaszewski
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

#include <mex.h>
#include <jni.h>
#include <string.h>

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {
    if (nrhs != 2 || mxGetNumberOfDimensions(prhs[0]) != 2 ||
            !mxIsNumeric(prhs[0]) || mxIsSparse(prhs[0]) ||
            mxIsComplex(prhs[0]) || !mxIsDouble(prhs[0]) ||
            strcmp(mxGetClassName(prhs[1]), "java.lang.Double[][]") ||
            mxGetM(prhs[0]) != mxGetM(prhs[1])) {
        mexErrMsgTxt("Usage: weka_fast_ml_array_to_java_array(src, dest), where src - 2D full real double array, dest - java.lang.Double[][] with the same dimensions");
    }

    JavaVM *vm = 0;
    JNIEnv *env = 0;
    int n;
    JNI_GetCreatedJavaVMs(&vm, 1, (jsize*) &n);
    if (n != 1) {
        mexErrMsgTxt("Couldn't find existing Java VM");
    }
    vm->GetEnv((void**) &env, JNI_VERSION_1_6);
    if (env == 0) {
        mexErrMsgTxt("Couldn't get Java environment");
    }
    char *d = (char*) mxGetData(prhs[1]);
	jobjectArray rows = *((jobjectArray*)(d + 16));

    size_t M = mxGetM(prhs[0]);
    size_t N = mxGetN(prhs[0]);
    const double *p = mxGetPr(prhs[0]);

    jclass java_lang_Double = env->FindClass("java/lang/Double");
    jmethodID java_lang_Double_init_D = env->GetMethodID(java_lang_Double, "<init>", "(D)V");

    for (int m = 0; m < M; m++) {
        jobjectArray col = (jobjectArray) env->GetObjectArrayElement(rows, m);
        if (env->GetArrayLength(col) != N) {
            mexErrMsgTxt("Wrong column length in destination");
        }
		for (int n = 0; n < N; n++) {
			jobject val = env->NewObject(java_lang_Double, java_lang_Double_init_D, *(p + m * N + n));
            env->SetObjectArrayElement(col, n, val);
            env->DeleteLocalRef(val);
		}
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *