Подтвердить что ты не робот

Как получить доступ к массивам внутри объекта с JNI?

Учебники JNI, например этот, достаточно хорошо описывают, как обращаться к примитивным полям внутри объекта, а также как обращаться к массивам, которые предоставляются как явные аргументы функции (т.е. как подклассы jarray). Но как получить доступ к массивам Java (примитивных), которые являются полями в jobject? Например, я хотел бы работать с массивом байтов следующего объекта Java:

class JavaClass {
  ...
  int i;
  byte[] a;
}

Основная программа может быть примерно такой:

class Test {

  public static void main(String[] args) {
    JavaClass jc = new JavaClass();
    jc.a = new byte[100];
    ...
    process(jc);
  }

  public static native void process(JavaClass jc);
}

Соответствующая сторона С++ будет:

JNIEXPORT void JNICALL Java_Test_process(JNIEnv * env, jclass c, jobject jc) {

  jclass jcClass = env->GetObjectClass(jc);
  jfieldID iId = env->GetFieldID(jcClass, "i", "I");

  // This way we can get and set the "i" field. Let double it:
  jint i = env->GetIntField(jc, iId);
  env->SetIntField(jc, iId, i * 2);

  // The jfieldID of the "a" field (byte array) can be got like this:
  jfieldID aId = env->GetFieldID(jcClass, "a", "[B");

  // But how do we operate on the array???
}

Я думал использовать GetByteArrayElements, но он хочет ArrayType в качестве аргумента. Очевидно, что я что-то упускаю. Есть ли способ сделать это?

4b9b3361

Ответ 1

Надеюсь, это вам немного поможет (посмотрите ссылку JNI Struct):

// Get the class
jclass mvclass = env->GetObjectClass( *cls );
// Get method ID for method getSomeDoubleArray that returns a double array
jmethodID mid = env->GetMethodID( mvclass, "getSomeDoubleArray", "()[D");
// Call the method, returns JObject (because Array is instance of Object)
jobject mvdata = env->CallObjectMethod( *base, mid);
// Cast it to a jdoublearray
jdoubleArray * arr = reinterpret_cast<jdoubleArray*>(&mvdata)
// Get the elements (you probably have to fetch the length of the array as well
double * data = env->GetDoubleArrayElements(*arr, NULL);
// Don't forget to release it 
env->ReleaseDoubleArrayElements(*arr, data, 0); 

Итак, я использую метод вместо поля (я считал, что вы вызываете очиститель Java getter), но вы, вероятно, можете переписать его и для полей. Не забудьте выпустить, и, как в комментарии, вам, вероятно, еще нужно будет получить длину.

Изменить: перепишите свой пример, чтобы получить его для поля. В основном заменить CallObjectMethod на GetObjectField.

JNIEXPORT void JNICALL Java_Test_process(JNIEnv * env, jclass c, jobject jc) {

  jclass jcClass = env->GetObjectClass(jc);
  jfieldID iId = env->GetFieldID(jcClass, "i", "I");

  // This way we can get and set the "i" field. Let double it:
  jint i = env->GetIntField(jc, iId);
  env->SetIntField(jc, iId, i * 2);

  // The jfieldID of the "a" field (byte array) can be got like this:
  jfieldID aId = env->GetFieldID(jcClass, "a", "[B");

  // Get the object field, returns JObject (because Array is instance of Object)
  jobject mvdata = env->GetObjectField (jc, aID);

  // Cast it to a jdoublearray
  jdoubleArray * arr = reinterpret_cast<jdoubleArray*>(&mvdata)

  // Get the elements (you probably have to fetch the length of the array as well  
  double * data = env->GetDoubleArrayElements(*arr, NULL);

  // Don't forget to release it 
  env->ReleaseDoubleArrayElements(*arr, data, 0);
}

Ответ 2

В gcc 6.3 я получаю предупреждение о том, что "разыменованный шрифт-указатель нарушит правила строгого сглаживания" из строки, подобной этой:

jdoubleArray arr = *reinterpret_cast<jdoubleArray*>(&mvdata);

Но так как jdoubleArray сам по себе является указателем на класс _jdoubleArray, нет необходимости получать адрес до кастинга, и этот static_cast работает без предупреждений:

jdoubleArray arr = static_cast<jdoubleArray>(mvdata);