当我从本机Java方法抛出C++异常时会发生什么?

Ida*_*n K 24 c++ java linux java-native-interface exception

假设我将Sun的JVM嵌入到C++应用程序中.通过JNI,我调用了一个Java方法(我自己的),它又调用了我在共享库中实现的本机方法.

如果此本机方法抛出C++异常会发生什么?

编辑:编译器是gcc 3.4.x,jvm是sun的1.6.20.

Moo*_*uck 24

Java编译器不理解C++异常,因此您必须使用Java和C++异常.幸运的是,这并不是太复杂.首先,我们有一个C++异常,告诉我们是否发生了Java异常.

#include <stdexcept>
//This is how we represent a Java exception already in progress
struct ThrownJavaException : std::runtime_error {
    ThrownJavaException() :std::runtime_error("") {}
    ThrownJavaException(const std::string& msg ) :std::runtime_error(msg) {}
};
Run Code Online (Sandbox Code Playgroud)

如果已经存在Java异常,则抛出C++异常的函数:

inline void assert_no_exception(JNIEnv * env) {
    if (env->ExceptionCheck()==JNI_TRUE) 
        throw ThrownJavaException("assert_no_exception");
}
Run Code Online (Sandbox Code Playgroud)

我们还有一个C++异常用于抛出新的Java异常:

//used to throw a new Java exception. use full paths like:
//"java/lang/NoSuchFieldException"
//"java/lang/NullPointerException"
//"java/security/InvalidParameterException"
struct NewJavaException : public ThrownJavaException{
    NewJavaException(JNIEnv * env, const char* type="", const char* message="")
        :ThrownJavaException(type+std::string(" ")+message)
    {
        jclass newExcCls = env->FindClass(type);
        if (newExcCls != NULL)
            env->ThrowNew(newExcCls, message);
        //if it is null, a NoClassDefFoundError was already thrown
    }
};
Run Code Online (Sandbox Code Playgroud)

我们还需要一个函数来吞下C++异常并用Java异常替换它们

void swallow_cpp_exception_and_throw_java(JNIEnv * env) {
    try {
        throw;
    } catch(const ThrownJavaException&) {
        //already reported to Java, ignore
    } catch(const std::bad_alloc& rhs) {
        //translate OOM C++ exception to a Java exception
        NewJavaException(env, "java/lang/OutOfMemoryError", rhs.what()); 
    } catch(const std::ios_base::failure& rhs) { //sample translation
        //translate IO C++ exception to a Java exception
        NewJavaException(env, "java/io/IOException", rhs.what()); 

    //TRANSLATE ANY OTHER C++ EXCEPTIONS TO JAVA EXCEPTIONS HERE

    } catch(const std::exception& e) {
        //translate unknown C++ exception to a Java exception
        NewJavaException(env, "java/lang/Error", e.what());
    } catch(...) {
        //translate unknown C++ exception to a Java exception
        NewJavaException(env, "java/lang/Error", "Unknown exception type");
    }
}
Run Code Online (Sandbox Code Playgroud)

使用上述函数,可以很容易地在C++代码中使用Java/C++混合异常,如下所示.

extern "C" JNIEXPORT 
void JNICALL Java_MyClass_MyFunc(JNIEnv * env, jclass jc_, jstring param)
{
    try { //do not let C++ exceptions outside of this function

        //YOUR CODE BELOW THIS LINE.  HERES SOME RANDOM CODE
        if (param == NULL) //if something is wrong, throw a java exception
             throw NewJavaException(env, "java/lang/NullPointerException", "param");            
        do_stuff(param); //might throw java or C++ exceptions
        assert_no_exception(env); //throw a C++ exception if theres a java exception
        do_more_stuff(param); //might throw C++ exceptions
        //prefer Java exceptions where possible:
        if (condition1) throw NewJavaException(env, "java/lang/NullPointerException", "condition1");
        //but C++ exceptions should be fine too
        if (condition0) throw std::bad_alloc("BAD_ALLOC");
        //YOUR CODE ABOVE THIS LINE.  HERES SOME RANDOM CODE

    } catch(...) { //do not let C++ exceptions outside of this function
        swallow_cpp_exception_and_throw_java(env);
    } 

}
Run Code Online (Sandbox Code Playgroud)

如果你真的很有抱负,那么就可以跟踪StackTraceElement[]你的更大功能,并获得部分堆栈跟踪.基本方法是给每个函数a StackTraceElement,当它们被调用时,将指向它们的指针推送到线程本地"callstack",当它们返回时,弹出指针.然后,更改构造函数NewJavaException以生成该堆栈的副本,​​并将其传递给setStackTrace.


Rob*_*edy 5

在JNI文献中," 异常 "一词似乎专门用于指代Java异常.本机代码中的意外事件称为编程错误.JNI显式不需要JVM来检查编程错误.如果发生编程错误,则行为未定义.不同的JVM可能表现不同.

将所有编程错误转换为返回代码或Java异常是本机代码的责任.不会从本机代码中立即抛出Java异常.它们可以处于挂起状态,只有在本机代码返回Java调用者后才会抛出.本机代码可以检查挂起的异常,ExceptionOccurred并清除它们ExceptionClear.