В настоящее время я работаю с Джинни и хотел бы вызывать методы 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
Как вы заметили, использование объекта, который реализует интерфейс Джинни, требует сначала создания объекта, что может быть сделано только на языке, который реализует объект. Получив объект, вы можете передавать его между языками и свободно вызывать его с любого языка. Вопрос в том, как вы «загрузитесь», чтобы получить нужный объект. В общем, начальная загрузка всегда должна начинаться с 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 для обратных вызовов. Особенности того, как это произойдет, вероятно, зависят от потребностей инициализации вашего приложения.
Других решений пока нет …