Метод JNI int, возвращающийся из исключения - PullRequest
0 голосов
/ 03 декабря 2011

Предположим, у меня есть такой класс Java:

    public class Test
    {
        static { System.loadLibrary("test"); }
        public native int foo();
    }

Предположим, что метод foo () выполняет некоторые вызовы JNI, и один из этих вызовов завершается неудачно (IE выдает исключение). Затем я хотел бы вернуться из кода JNI и получить исключение в Java. Например:

    jint Java_Test_foo(JNIEnv* env, jobject thiz)
    {
        jstring foobar = (*env)->NewStringUTF(env, "Hello from JNI !");
        if(foobar == NULL) // Not enough memory. OutOfMemoryError is thrown
            return NULL; // Return immediately to get exception thrown in Java
        // Do other stuff
        ...
        return some_computed_jint;
    }

Проблема в том, что return NULL - это не джинт. Например, в Android я получаю это предупреждение при компиляции: предупреждение: return возвращает целое число из указателя без приведения .

Теперь вопрос: Что я должен вернуть в случае, если Исключение выдается внутри метода JNI, который возвращает jint?

Ответы [ 2 ]

4 голосов
/ 03 декабря 2011

Если ваш код (или библиотека) выдает Exception в Java, не имеет значения, какое значение вы возвращаете, Java его проигнорирует. Очевидно, что это должен быть совместимый тип - поэтому возвращение 0 в вашем примере, похоже, имеет смысл, или что вам удобно. Когда ваш код вернется, среда выполнения Java заметит, что Exception был поднят, и продолжит распространять его, и проигнорирует возвращаемое значение вашей функции.

Вам, конечно, нужно будет вернуть совместимый тип. Не просто возвращайте NULL, так как это будет приведено к int, когда функция не объявлена ​​для возврата указателя, что может быть неуместно.

Очевидно, однако, что когда вы вызываете функции C, они не вызовут Exception. Таким образом, вы можете либо сопоставить целое число с ошибкой (например, -1), а затем выбросить Exception в Java, либо вы можете потратить время на создание Exception в JNI.

0 голосов
/ 08 марта 2013

РЕДАКТИРОВАТЬ: См. Также этот элегантный ответ с использованием функции вместо макроса нижнего препроцессора .


Я приведу пример для завершения Ответ Эдварда Томсона .

В этом примере непустые функции JNI return 0;

JNIEXPORT jlong JNICALL Java_group_package_class_function1(
                           JNIEnv *env, jobject object, jlong value)
{
  try 
  {
    /* ... my processing ... */
    return jlong(result);
  }
  CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION
  return 0;
}

JNIEXPORT jstring JNICALL Java_group_package_class_function2(
                            JNIEnv *env, jobject object, jlong value)
{
  try 
  {
    /* ... my processing ... */
    jstring jstr = env->NewStringUTF("my result");
    return  jstr;
  }
  CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION
  return 0;
}

JNIEXPORT void JNICALL Java_group_package_class_function3(
                           JNIEnv *env, jobject object, jlong value)
{
  try 
  {
    /* ... my processing ... */
  }
  CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION
}  // void function => no "return 0;" statement

Макрос препроцессора C CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION присутствует в конце всех вышеуказанных функций JNI.

#define CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION              \
                                                                  \
  catch (const package::Exception& e)                             \
  {                                                               \
    jclass jc = env->FindClass("group/package/Exception");        \
    if(jc) env->ThrowNew (jc, e.what());                          \
    /* if null => NoClassDefFoundError already thrown */          \
  }                                                               \
  catch (const std::bad_alloc& e)                                 \
  {                                                               \
    /* OOM exception */                                           \
    jclass jc = env->FindClass("java/lang/OutOfMemoryError");     \
    if(jc) env->ThrowNew (jc, e.what());                          \
  }                                                               \
  catch (const std::ios_base::failure& e)                         \
  {                                                               \
    /* IO exception */                                            \
    jclass jc = env->FindClass("java/io/IOException");            \
    if(jc) env->ThrowNew (jc, e.what());                          \
  }                                                               \
  catch (const std::exception& e)                                 \
  {                                                               \
    /* unknown exception */                                       \
    jclass jc = env->FindClass("java/lang/Error");                \
    if(jc) env->ThrowNew (jc, e.what());                          \
  }                                                               \
  catch (...)                                                     \
  {                                                               \
    /* Oops I missed identifying this exception! */               \
    jclass jc = env->FindClass("java/lang/Error");                \
    if(jc) env->ThrowNew (jc, "unexpected exception");            \
  }
...