Какие классы и методы мне нужно использовать для вызова Java из C++?
В настоящее время я работаю с Джинни и хотел бы вызывать методы Java из C++.
У меня есть следующий файл описания интерфейса:
ExampleSO = interface +j {
PerformAddition(a: i32, b: i32): i32;
}
Он генерирует эти файлы:
src/main/cpp/ExampleSO.hpp
: C++ExampleSO
класс, содержащий виртуальный деструктор и виртуальныйPerformAddition
метод.src/main/java/com/name/group/ExampleSO.java
: ДжаваExampleSO
абстрагировать класс, содержащийpublic abstract PerformAddition
метод.src/main/jni/NativeExampleSO.hpp
/.cpp
: JNI привязки.
Что я хочу сделать, это создать новый класс Java, который будет расширять ExampleSO
Класс Java (как указано в описании интерфейса с +j
) и иметь возможность вызывать эти методы из файла C++.
Я могу видеть в привязках JNI, что есть общественная using CppType = std::shared_ptr<::ExampleSO>;
, Учитывая имя, я предположил, что это будет способ вызова методов Java через мост JNI, но это приводит к segfault, когда я пытаюсь сделать что-то вроде:
// SampleClass.hpp
#include "ExampleSO.hpp"
class SampleClass: ExampleSO {
private:
NativeExampleSO::CppType neso;
public:
int32_t PerformAddition(int32_t a, int32_t b) override;
}
// SampleClass.cpp
#include "SampleClass.hpp"
int32_t SampleClass::PerformAddition(int32_t a, int32_t b) {
neso->PerformAddition(a, b); // Crash
}
Должен ли я инициализировать это neso
поле каким-то образом?
Заранее спасибо.
Редактировать: вот содержание NativeExampleSO.hpp
(Мост JNI), это может облегчить ответ:
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from ExampleSO.djinni
#pragma once
#include "ExampleSO.hpp"
#include "djinni_support.hpp"
namespace djinni_generated {
class NativeExampleSO final : ::djinni::JniInterface<::ExampleSO, NativeExampleSO> {
public:
using CppType = std::shared_ptr<::ExampleSO>;
using CppOptType = std::shared_ptr<::ExampleSO>;
using JniType = jobject;
using Boxed = NativeExampleSO;
~NativeExampleSO();
static CppType toCpp(JNIEnv* jniEnv, JniType j) { return ::djinni::JniClass<NativeExampleSO>::get()._fromJava(jniEnv, j); }
static ::djinni::LocalRef<JniType> fromCppOpt(JNIEnv* jniEnv, const CppOptType& c) { return {jniEnv, ::djinni::JniClass<NativeExampleSO>::get()._toJava(jniEnv, c)}; }
static ::djinni::LocalRef<JniType> fromCpp(JNIEnv* jniEnv, const CppType& c) { return fromCppOpt(jniEnv, c); }
private:
NativeExampleSO();
friend ::djinni::JniClass<NativeExampleSO>;
friend ::djinni::JniInterface<::ExampleSO, NativeExampleSO>;
class JavaProxy final : ::djinni::JavaProxyHandle<JavaProxy>, public ::ExampleSO
{
public:
JavaProxy(JniType j);
~JavaProxy();
int32_t PerformAddition(int32_t a, int32_t b) override;
private:
friend ::djinni::JniInterface<::ExampleSO, ::djinni_generated::NativeExampleSO>;
};
const ::djinni::GlobalRef<jclass> clazz { ::djinni::jniFindClass("com/name/group/ExampleSO") };
const jmethodID method_PerformAddition { ::djinni::jniGetMethodID(clazz.get(), "PerformAddition", "(II)I") };
};
} // namespace djinni_generated
1 ответ
Как вы заметили, использование объекта, реализующего интерфейс Джинни, требует сначала создания объекта, что может быть сделано только на языке, реализующем объект. Получив объект, вы можете передавать его между языками и свободно вызывать его с любого языка. Вопрос в том, как вы "загрузитесь", чтобы получить нужный объект. В общем, начальная загрузка всегда должна начинаться с Java/ObjC.
Джинни не поддерживает прямое использование конструкций, но поддерживает статические методы в одном направлении (Java/ObjC -> C++). Вы можете либо сделать вызов из Java, чтобы предоставить объект для C++ для хранения и использования позже, либо вы можете сделать обратное и использовать статический метод в качестве фабрики, позволяя Java запрашивать C++ для создания объекта. Первый вариант проще, если вы не возражаете против использования глобального состояния или вам нужно немедленно использовать объект.
interface example_so_setup +c {
set_example_so(obj: example_so)
}
В тестовом наборе Джинни есть пример, где test_helper
это интерфейс с методом check_client_interface_ascii
который вызывается из Java здесь. Java передает объект aJava в качестве аргумента, который затем вызывает C++.
Если вы хотите избежать использования глобального состояния, вы можете просто добавить дополнительный шаг, где Java сначала вызывает метод статической фабрики для создания какого-либо объекта "менеджер" C++, а затем вызывает этот объект, чтобы передать example_so для обратных вызовов. Особенности того, как это произойдет, вероятно, зависят от потребностей инициализации вашего приложения.