Reputation: 5300
In my native C++ code, there is a function that must return a Java object. This function is called from within the Java code using JNI. Inside the returned object, there is a two dimensional double array field that must be filled before the object is returned. While there are example codes regarding setting an array field inside a jobject, there is no clear way of setting multidimensional array fields, whether they be arrays of primitives or other objects. How exactly does one achieve this?
Upvotes: 3
Views: 3882
Reputation: 5300
After researching a bit, I found that the trick is to treat the multidimensional array field as an array of jobjects. In this example, these jobjects are in fact arrays of doubles. This can be continued for as many dimensions as needed. The object that contains the multidimensional array field in question is:
package com.example;
public class ObjectTransform{
public String name;
public double[][] transform;
public ObjectTransform(){
name = "";
transform = new double[4][4];
for(int i=0;i<4;i++)
for(int j=0;j<4;j++)
transform[i][j] = i == j ? 1 : 0;
}
}
where the multidimensional array is in fact a 4 by 4 transform matrix. Just to prove the necessity of having an object containing the array, there is another String field. Having this, the native C++ function that returns an ObjectTransform
is the following:
extern "C" JNIEXPORT jobject JNICALL Java_com_example_examplefunction(
JNIEnv* env, jclass* class_){
//Get class identifier of our object
jclass cls = env->FindClass("com/example/ObjectTransform");
//Get constructor identifier for our object
jmethodID constructor = env->GetMethodID(cls,"<init>","()V");
//Get field identifiers of our object
//Transform is a two dimensional double array, denoted as [[D
jfieldID nameID = env->GetFieldID(cls,"name","Ljava/lang/String;");
jfieldID transformID = env->GetFieldID(cls,"transform","[[D");
//Allocate object that we will return
jobject jresult = env->NewObject(cls, constructor);
//Set object name
env->SetObjectField(jresult, nameID, env->NewStringUTF("some name"));
/*
* Build object transform matrix
*/
//Get rows of the matrix in JVM heap space, we will change them
jobjectArray rows = reinterpret_cast<jobjectArray>(
env->GetObjectField(jresult,transformID));
//Allocate some temporary variables
jdoubleArray jrow;
jdouble row[4];
//Traverse rows
for(int j=0;j<4;j++){
//Traverse columns
for(int k=0;k<4;k++){
//Set current element of the matrix accordingly
row[k] = calculate_some_value(j,k);
}
//Temporarily allocate a new row in JVM heap space
//No need to unpin an array allocated with New...Array in the end
jrow = env->NewDoubleArray(4);
//Fill the newly allocated row
env->SetDoubleArrayRegion(jrow,0,4,row);
//Write the newly filled row to the appropriate row of our matrix
env->SetObjectArrayElement(rows,j,jrow);
}
return jresult;
}
The native function can be called from inside Java as usual with the following signature:
package com.example;
...
private native ObjectTransform examplefunction();
From here, setting multidimensional array fields of arbitrary objects follows straightforwardly: Instead of [[D
, write [[com.example.exampleclass;
. Instead of NewDoubleArray
, call NewObjectArray
. Instead of SetDoubleArrayRegion
, set all elements of the object array individually by SetObjectArrayElement
to objects you have created beforehand.
All improvements on this code are of course welcome.
Upvotes: 4