In one of my projects, I had the need to call a Java function from a C++ class and somewhat surprisingly it was more complicated than I initially expected it to be. Arguably there are more resources on how to call C++ functions from a Java class then the other way around. Also one needs to ensure that the documentation refers to calling Java from C++ instead of C, as there differences in the function calls. Hence based on my experience here’s the solution that I came up with.

Requirements

I wanted a Java class to perform a certain task and return an array of double (primitive type). I would then invoke this Java method from my C++ class and use the values of that double array.
A sample of my Java class
public class Test{ 
public[] double array;
public int size;
public double[] getArray() {
array = new double[]{1.2,3.2,1.2}; return array;
}
}

Hurdles and initial mistakes

Initially what I was trying was to invoke the method using the code below which was causing a segmentation fault i.e. segfault (a term we all know and love)

void getArrayFromJava(JNIEnv* env)
{
jmethodID method;
jclass testClass = env->FindClass("test");
method = env->GetMethodID(testClass,"getArray","()Ljava/lang/Object");
//below is the point in code where the segmentation fault in the jvm occured
jdoubleArray r = (jdoubleArray)env->CallObjectMethod(testClass,method,NULL);
return r;
}

At first I thought it was because I was calling an object method directly without instantiating the object, however even when I did change the getArrayFromJava method to a static method, the same segfault occurred.

My Solution

So what did I do? Well, I declare the double array as well as the size(arraySize) of the array as class variables and that are set when instantiating the Java class i.e. via values passed in the constructor.
the java code being called
 class Test{

public double[] array = null;
public int arraySize;
public String str;
public Test(String value,int val)
{
str = value;
setArray(val);
arraySize = 3;
}
public void setArray(int val)
{
double []db = new double[]{val,val,1.4};
this.array = db;
}
}
Below is the C++ code that invokes the uses the Java class Test. To keep this post brief and to focus on the main objective of this post, I will not going into details of creating a pointer to the JNIEnv. You could get that info from one of the links in the References used section of this post or those of you reading this post, think that this post is incomplete without it, please let me know and I will see what I can do to add that info here.
void getArrayFromJava(JNIEnv* env)
{
jclass testClass;
jobject testObj;
jmethodID constructor;
const char* utfChars;
testClass = env->FindClass("Test");//get the class

//get the constructor for the java class by using <init>
constructor = env->GetMethodID(testClass,"<init>","(Ljava/lang/String;I)V");
jstring stringArgs = env->NewStringUTF("nTest sample");
jint intParam = 1;
testObj = env->NewObject(testClass,constructor,stringArgs,intParam);//instantiate a class object

//get the array field from the object
jobject tArr = env->GetObjectField(testObj,env->GetFieldID(testClass,"array","[D"));

//get the array size field from the object
jint arraySize = (jint)env->GetIntField(testObj,env->GetFieldID(testClass,"arraySize","I"););

jdoubleArray *arr = reinterpret_cast<jdoubleArray*>(&tArr);
//now we have a pointer to the array and a size which tells us of the number of elements

double* data = env->GetDoubleArrayElements(*arr,NULL);
/*accessing a pointer to a double array is now simple and can be done using the information in 'data' and array size' */
}

References used

http://java.sun.com/docs/books/jni/html/objtypes.html#4013

http://www.codeproject.com/Articles/22881/How-to-Call-Java-Functions-from-C-Using-JNI

http://java.sun.com/developer/onlineTraining/Programming/JDCBook/jniexamp.html#run

Categories: C++JavaJNI

Leave a Reply

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