java - 当我从 native Java 方法抛出 C++ 异常时会发生什么?

标签 java c++ linux exception java-native-interface

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

如果 native 方法抛出 C++ 异常会怎样?

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

最佳答案

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

以及一个在 Java 异常已经存在时抛出 C++ 异常的函数:

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

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

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

我们还需要一个函数来吞下 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");
    }
}

有了上面的函数,就可以很容易的在你的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);
    } 

}

如果您真的雄心勃勃,则可以跟踪更大函数的 StackTraceElement[],并获得部分堆栈跟踪。基本方法是给每个函数一个 StackTraceElement,当它们被调用时,将指向它们的指针推送到线程本地“调用堆栈”上,当它们返回时,将指针弹出。然后,更改 NewJavaException 的构造函数以复制该堆栈,并将其传递给 setStackTrace

关于java - 当我从 native Java 方法抛出 C++ 异常时会发生什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38034907/

相关文章:

java - 使用 java 的以下场景的最佳解决方案?

java - 使用SimpleBrokerMessageHandler时的通信关系 - STOMP Spring

java - 如何在构建步骤中配置自动提交

c++ - 杂注警告(禁用 : 4700 ) not working in Visual Studio Express 2013

linux - 嵌入式 linux 上的 QT5 eglfs(TI am355x evm 入门套件)

java - Wicket 1.5(.2) 中的 url 更改时组件未重新加载?

c++ - 在Ubuntu 16.04上编译Armadillo C++库时出错

C++如何在字符后获取子字符串?

linux - 如何制作带有并排选项的 linux 对话框实用程序菜单?

c++ - windows 和 unix/linux 下的 Socket 编程混淆