Trình biên dịch Java không hiểu ngoại lệ C++, vì vậy bạn sẽ phải làm việc với cả hai ngoại lệ Java và C++. May mắn thay, điều đó không quá phức tạp. Đầu tiên chúng ta có một ngoại lệ C++ cho chúng ta biết nếu một ngoại lệ Java đã xảy ra.
#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) {}
};
và một chức năng để ném một ++ ngoại lệ C nếu một ngoại lệ của Java đã được tại chỗ:
inline void assert_no_exception(JNIEnv * env) {
if (env->ExceptionCheck()==JNI_TRUE)
throw ThrownJavaException("assert_no_exception");
}
chúng tôi cũng có một C++ ngoại lệ cho ném ngoại lệ Java mới:
//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
}
};
Chúng ta cũng cần một hàm để nuốt các ngoại lệ C++ và thay thế chúng bằng ngoại lệ 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");
}
}
Với các chức năng trên, thật dễ dàng để sử dụng các ngoại lệ lai Java/C++ trong mã C++ của bạn, như được hiển thị bên dưới.
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);
}
}
Nếu bạn thực sự đầy tham vọng, nó có thể theo dõi một StackTraceElement[]
chức năng lớn hơn của bạn, và nhận được một stacktrace một phần. Phương pháp cơ bản là cung cấp cho mỗi hàm StackTraceElement
và khi chúng được gọi, hãy đẩy con trỏ đến một hàm "callstack" cục bộ và khi chúng trở lại, hãy tắt con trỏ. Sau đó, thay đổi hàm tạo của NewJavaException
để tạo bản sao của ngăn xếp đó và chuyển nó tới setStackTrace
.
Nếu bạn để nó phổ biến, bạn có thể nhận được phản hồi "hoảng loạn" VM. Tại sao không chỉ thử nó? –
Nền tảng nào? Có thể là trình biên dịch cụ thể ... –
@Michael: Tôi nghĩ "Java" là một nền tảng? – sbi