Метод JNI int, возвращающийся из исключения
Предположим, у меня есть такой класс 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 я получал бы это предупреждение при компиляции:warning: return возвращает целое число из указателя без приведения.
Теперь вопрос: что я должен вернуть в случае, если Исключение выдается внутри метода JNI, который возвращает jint?
2 ответа
Если ваш код (или библиотека) поднимает Exception
в Java не имеет значения, какое значение вы возвращаете, Java его игнорирует. Очевидно, что это должен быть совместимый тип - поэтому возвращение 0
в вашем примере, кажется, имеет смысл, или как вам удобно. Когда ваш код вернется, среда выполнения Java заметит, что Exception
был поднят и продолжает распространять его и игнорирует возвращаемое значение вашей функции.
Вам, конечно, нужно будет вернуть совместимый тип. Не просто вернуться NULL
, как это будет приведено к int
когда функция не объявлена для возврата указателя, что может не подходить.
Очевидно, однако, что когда вы вызываете функции C, они не будут вызывать Exception
, Таким образом, вы можете сопоставить целое число с ошибкой (-1
например), а затем бросить Exception
в Java, или вы можете потратить время на создание Exception
в JNI.
РЕДАКТИРОВАТЬ: См. Также этот элегантный ответ с использованием функции вместо нижнего макроса препроцессора.
Я приведу пример, чтобы завершить ответ Эдварда Томсона.
В этом примере не пустые функции 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"); \
}