vendredi 27 mars 2015

How to send a 2D array from C++ and edit the values in Java and send back to C++ in 2D array via JNI


As per subject, I have a 2D array named cells which contains some value in C++.


I would like to send this 2D array over to my Java file which will replace some of this values. Is there a way I could do in Java that "receives" the array from C++ ?


Currently I'm able to print my contents from Java from C++. My source code is below:


C++ Source Code :



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


Do I have to include some code within my Java program to "receive" the cell 2D array values from C++? I have searched the whole night but unable to get this working. Please help !!!




Aucun commentaire:

Enregistrer un commentaire