JNI int 方法从异常返回

发布于 2024-12-19 12:02:47 字数 808 浏览 0 评论 0原文

假设我有一个像这样的 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 不是 jint。以 Android 为例,我在编译时会收到此警告: 警告:返回从指针生成整数,无需强制转换

现在的问题是:如果在返回 jint 的 JNI 方法中抛出异常,我应该返回什么?

Suppose I have a Java class like this:

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

Suppose that the foo() method is doing some JNI calls, and one of these calls fails (IE, throws an exception). I would then like to return from the JNI code and have the exception thrown in Java. For example:

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

The problem is that return NULL is not a jint. In Android for example, I would get this warning when compiling:
warning: return makes integer from pointer without a cast.

Now the question is: What should I return in case of an Exception being thrown inside a JNI method that returns a jint?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

星星的轨迹 2024-12-26 12:02:47

如果您的代码(或库)在 Java 中引发Exception,那么无论您返回什么值,Java 都会忽略它。显然它需要是兼容的类型 - 因此在您的示例中返回 0 似乎是有意义的,或者您喜欢的任何类型。当您的代码返回时,Java 运行时将注意到已引发Exception 并继续传播它并忽略函数的返回值。

当然,您需要返回兼容的类型。不要简单地返回NULL,因为当函数未声明为返回指针时,它将被转换为int,这可能不合适。

但显然,当您调用 C 函数时,它们不会引发 Exception。因此,您可以将整数映射到错误条件(例如 -1),然后在 Java 中抛出 Exception,或者您可以花时间构建一个 >JNI 中的异常

If your code (or a library) raises an Exception in Java, it doesn't matter what value you return, Java will ignore it. Obviously it needs to be a compatible type - so returning 0 in your example would seem to make sense, or whatever you're comfortable with. When your code returns, the Java runtime will notice that an Exception has been raised and continue to propagate it and ignore the return value of your function.

You will, of course, need to return a compatible type. Don't simply return NULL, as that will be cast to an int when the function is not declared to return a pointer, which may not be appropriate.

Obviously, though, when you're calling C functions, those would not raise an Exception. So you can either map an integer to an error condition (-1 for example) and then throw the Exception in Java, or you can take the time to build an Exception in JNI.

为人所爱 2024-12-26 12:02:47

编辑:另请参阅这个优雅的答案,使用函数而不是底部预处理器宏


我提供了一个示例来完成爱德华·汤姆森的回答

在此示例中,JNI 非 void 函数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");            \
  }

EDIT:   See also this elegant answer using a function instead of the bottom preprocessor macro.


I provide an example to complete the Edward Thomson's answer.

In this example the JNI non-void functions 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

The C preprocessor macro CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION is present at the end of all the above JNI functions.

#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");            \
  }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文