Il compilatore Java non comprende le eccezioni C++, quindi dovrai lavorare con entrambe le eccezioni Java e C++. Fortunatamente, non è eccessivamente complicato. Per prima cosa abbiamo un'eccezione C++ che ci dice se si è verificata un'eccezione 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) {}
};
e una funzione per generare un'eccezione C++ se un'eccezione Java è già in atto:
inline void assert_no_exception(JNIEnv * env) {
if (env->ExceptionCheck()==JNI_TRUE)
throw ThrownJavaException("assert_no_exception");
}
abbiamo anche un'eccezione C++ per lanciare nuove eccezioni 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
}
};
Abbiamo anche bisogno di una funzione per invertire le eccezioni C++ e sostituirle con le eccezioni 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");
}
}
Con le funzioni di cui sopra, è facile utilizzare le eccezioni ibride Java/C++ nel codice C++, come mostrato di seguito.
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);
}
}
Se siete veramente ambiziosi, è possibile tenere traccia di un StackTraceElement[]
delle vostre funzioni più grandi, e ottenere uno stacktrace parziale. Il metodo di base è di assegnare a ciascuna funzione uno StackTraceElement
e, come vengono chiamati, spingere un puntatore su di essi su un "callstack" locale del thread e quando ritornano, spegne il puntatore. Quindi, modificare il costruttore di NewJavaException
per creare una copia di tale stack e passarlo a setStackTrace
.
Se lo si lascia propagare, probabilmente si ottiene una risposta "panica" della VM. Perché non provarlo? –
Quale piattaforma? Potrebbe essere specifico del compilatore ... –
@ Michael: Pensavo che "Java" fosse una piattaforma? – sbi