Очередная статья, которую я не хочу ставить в оппозицию к уже существующим языковым средам и показывать ее преимущества. Ява платформа со своим многолетним опытом и прекрасной базой ее создателей практически не оставляет себе конкурентов и является образцом взаимодействия, которое только себе можно представить с любой другой внешней средой. Без труда можно получить все решения доступные программисту без всяких усилий, в то время как в среде ерангла о таких возможностях приходится только мечтать. Нельзя оставить без внимания такой важный момент как возможность инициализировать состояние виртуальной машины из среды С++ и передать ей управление! Что очень напоминает нам все современные шумные решения последних лет от sun (javafx), adobe (flash) и google (native client). Но давайте остановимся, я не буду позволять себе больше высказывать свою позицию относительно эффективного решения ниже этого абзаца, которое не нуждается в модификации уже далеко за первый десяток лет!
Взаимодействие явы с языком С++ осуществляется через набор библиотек JNI, интерфейсы которой открывают всю виртуальную машину со всеми ее внутренними особенностями разработчику. Все интерфейсы подробно описаны в документации и представляют из себя полноценную документацию по которой можно разобраться в тонкостях взаимодействия с виртуальной средой.
Взаимодействие ява среды с С++ происходит через создание оберток test_cpp.java && test_cpp.cpp двух файлов. В них происходит преобразования типов данных и образование новых структур для прозрачной передачи их между двумя средами.
Первым делом идут два библиотечных файла test.cpp && test.h, которые были представлены в предудущих статьях. Наша задача постараться использовать эти библиотеки из среды ява.
test.cpp
#include "test.h"
int test1(int i)
{
return i*2;
}
Res test2(int i, int k)
{
return Res(i*2,k*2);
}test.h
struct Res
{
int i,k;
Res(int ii,int kk):i(ii),k(kk){}
};
int test1(int i);
Res test2(int i, int k);Приложение на яве представляет из себя слегка модифицированный код стандратной процедуры main, с тем только отличием, что теперь это выполняется на языке ява.
app.java
public class app {
public static void main(String args[]) {
System.out.println(test_cpp.wr_test1(2));
test_cpp.ResJ c = test_cpp.wr_test2(2,4);
System.out.println(c.i + "; " + c.k);
}
}Для успешного вызова test_cpp.wr_test1 нам необходимо создать совместимый с виртуальной машиной класс на том же языке для обертывания нашей библиотеки test.cpp. Все функции, которые мы будем вызывать из виртуальной среды java мы называем в коде native. Это позволяет исполняемой среде подгружать нужную библиотеку и вызывать функцию написанную на других языках (в нашем примере на С++).
test_cpp.java
public class test_cpp {
public static class ResJ {
public int i,k;
};
static native int wr_test1(int i);
static native ResJ wr_test2(int i, int k);
static {
System.loadLibrary("test_cpp");
}
}Далее привидем пример реалзиации функций native. В данном примере я так же нарочно включаю лишние нам проверки на ошибки, которые пригодятся любому программисту при написании серезных проектов.
Если вы решите создавать по привиденной ниже схеме решения, обязательно соблюдайте все предосторожности рассмотренные в коде и не успускайте лишнюю возможность проверить результат исполнения команды JNI. Любой вызов должен сопровождаться проверкой на состояние ошибки в среде ява. В случае возникновения критической ситуации немедленно прекращайте выполнение программы через систему обработки исключений.
Существует поверие, что на ентерпрайз системах необходимо работать в режиме игнорировани ошибок и подробного их журналирования в системный лог, вместо остановки системы. Это грубейшее нарушение безопасности, о котором я подробно напишу в других статьях! Никогда не игнорируйте ошибки!
test_cpp.cpp
#include "test_cpp.h"
#include "test.h"
#include <exception>
class JavaException:public std::exception
{
public:
JNIEnv * m_env;
jthrowable m_exp;
JavaException(const JavaException&je)
{
m_env=je.m_env;
m_exp=je.m_exp;
}
JavaException(JNIEnv * env):m_env(env)
{
m_exp=(jthrowable)m_env->NewGlobalRef(m_env->ExceptionOccurred());
m_env->ExceptionDescribe();
}
~JavaException() throw()
{
m_env->DeleteGlobalRef(m_exp);
}
const char* what() const throw()
{
return "Java Exception";
}
};
JNIEXPORT jint JNICALL Java_test_1cpp_wr_1test1 (JNIEnv *env, jclass test_cpp_class, jint i_p)
{
try
{
return test1(i_p);
}catch(JavaException &e)
{
env->Throw(e.m_exp);
return 0;
}catch(std::exception &e)
{
env->ThrowNew(env->FindClass("java/lang/Throwable"),e.what());
return 0;
}
}
JNIEXPORT jobject JNICALL Java_test_1cpp_wr_1test2 (JNIEnv *env, jclass test_cpp_class, jint i_p, jint k_p)
{
try
{
Res res = test2(i_p, k_p);
jclass resj_class=env->FindClass("test_cpp$ResJ");
if(env->ExceptionOccurred())
throw JavaException(env);
jmethodID resj_init=env->GetMethodID(resj_class,"","()V");
if(env->ExceptionOccurred())
throw JavaException(env);
jobject res_obj=env->NewObject(resj_class,resj_init);
if(env->ExceptionOccurred())
throw JavaException(env);
jfieldID i_id=env->GetFieldID(resj_class,"i","I");
if(env->ExceptionOccurred())
throw JavaException(env);
env->SetIntField(res_obj,i_id,res.i);
if(env->ExceptionOccurred())
throw JavaException(env);
jfieldID k_id=env->GetFieldID(resj_class,"k","I");
if(env->ExceptionOccurred())
throw JavaException(env);
env->SetIntField(res_obj,k_id,res.k);
if(env->ExceptionOccurred())
throw JavaException(env);
return res_obj;
}catch(JavaException &e)
{
env->Throw(e.m_exp);
return jobject();
}catch(std::exception &e)
{
env->ThrowNew(env->FindClass("java/lang/Throwable"),e.what());
return jobject();
}
} test_cpp.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#ifndef __test_1cpp__
#define __test_1cpp__
#ifdef __cplusplus
extern "C"
{
#endif
JNIEXPORT jint JNICALL Java_test_1cpp_wr_1test1 (JNIEnv *env, jclass, jint);
JNIEXPORT jobject JNICALL Java_test_1cpp_wr_1test2 (JNIEnv *env, jclass, jint, jint);
#ifdef __cplusplus
}
#endif
#endif /* __test_1cpp__ */Исполняемый файл, запускает приложение.
test_cpp.sh
#!/bin/bash java -classpath . app


0 коммент.:
Отправить комментарий