Подтвердить что ты не робот

Что происходит, когда я бросаю исключение С++ из собственного метода Java?

Предположим, что я встраиваю Sun JVM в приложение на С++. Через JNI я вызываю метод Java (мой собственный), который по очереди вызывает собственный метод, который я реализовал в общей библиотеке.

Что произойдет, если этот собственный метод выдает исключение С++?

edit: компилятор gcc 3.4.x, jvm is sun 1.6.20.

4b9b3361

Ответ 1

В литературе JNI исключение слова используется исключительно для обозначения исключений Java. Неожиданные события в собственном коде называются ошибками программирования. JNI явно не требует JVM для проверки ошибок программирования. Если возникает ошибка программирования, поведение undefined. Различные JVM могут вести себя по-разному.

Собственная ответственность за перевод всех ошибок программирования в коды возврата или исключения Java. Исключения Java не получают сразу же из собственного кода. Они могут быть отложены, только после того, как исходный код вернется к Java-вызову. Нативный код может проверять ожидающие исключения с помощью ExceptionOccurred и очистить их с помощью ExceptionClear.

Ответ 2

Компилятор Java не понимает исключения С++, поэтому вам придется работать как с исключениями Java, так и с С++. К счастью, это не слишком сложно. Во-первых, мы имеем исключение С++, которое сообщает нам, произошло ли исключение 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 уже существует:

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

у нас также есть исключение С++ для бросания новых исключений 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
    }
};

Нам также нужна функция для проглатывания исключений С++ и замены их на исключения 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");
    }
}

С помощью вышеперечисленных функций легко использовать гибридные исключения Java/С++ в коде С++, как показано ниже.

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[] ваших больших функций и получать частичную стеклу. Основной метод состоит в том, чтобы дать каждой функции a StackTraceElement, и, как они называются, надавите на них указатель на локальный "стоп-стол" с потоком, и когда они вернутся, выньте указатель. Затем измените конструктор NewJavaException, чтобы сделать копию этого стека, и передайте его setStackTrace.

Ответ 3

Я предполагаю, что ваш JVM сработает. Исходные исключения С++ не распространяются на Java через JNI. Одной из причин этого является то, что JNI является интерфейсом C, а C ничего не знает о С++ исключениях.

Что вам нужно сделать, так это перехватить исключения С++, прежде чем вы войдете в С-код вашего JNI-кода и сделаете функцию JNI C возвратом кода ошибки. Затем вы можете проверить код ошибки внутри Java и при необходимости выбросить исключение Java.

Ответ 4

Я бы назвал это как поведение undefined. Распространение исключений обратно на C-код (то, что работает JVM) - это поведение undefined.

В Windows компиляторы должны использовать Microsoft Structured Exception Handling для реализации исключений, поэтому исключения С++ будут "безопасно" кариться с помощью кода C. Однако, этот код C не написан с учетом исключений, поэтому вы получите крах, если вам повезет, и непоследовательные утечки состояния и ресурсов, если вы не.

На других платформах, ну, я не знаю, но это не может быть красивее. Когда я пишу код JNI, я обертываю каждую функцию С++ в блок try: даже если я не throw, я все же могу получить некоторые из стандартных исключений (std::bad_alloc приходит на ум, но другие возможны тоже).

Ответ 5

JNI использует c-функции для взаимодействия с собственным кодом. C не может правильно обрабатывать исключения, так как он не знает об их существовании. Таким образом, вы должны поймать исключения в своем исходном коде и преобразовать их в исключения Java или ваш jvm сработает. (Это работает, так как исключение java генерируется только после возвращения собственного кода в java)