简体   繁体   中英

What classes and methods do I have to use to call Java from C++?

I'm currently working with Djinni and would like to call Java methods from C++.

I have the following interface description file:

ExampleSO = interface +j {
  PerformAddition(a: i32, b: i32): i32;
}

It generates these files:

  • src/main/cpp/ExampleSO.hpp : C++ ExampleSO class containing a virtual destructor and a virtual PerformAddition method.
  • src/main/java/com/name/group/ExampleSO.java : Java ExampleSO abtract class containing a public abstract PerformAddition method.
  • src/main/jni/NativeExampleSO.hpp / .cpp : JNI bindings.

What I want to do is create a new Java class that will extend the ExampleSO Java class (as specified in the interface description with +j ), and be able to call these methods from a c++ file.

I can see in the JNI bindings that there is a public using CppType = std::shared_ptr<::ExampleSO>; . Given the name, I assumed that this would be the way to call Java methods through the JNI bridge, but it results in a segfault when I'm trying to do something like :

// 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
}

Do I have to initialize this neso field in some way?

Thanks in advance.

Edit: Here is the content of NativeExampleSO.hpp (JNI bridge), it could make answering easier :

// 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

As you've noticed, using an object which implements a Djinni interface requires first creating an object, which can only be done in the language which implements the object. Once you've got an object you can pass it between languages, and make calls on it freely from any language. The question is how do you "bootstrap" to get the object you need. In general, bootstrapping always had to start from Java/ObjC.

Djinni doesn't support direct use of constructors, but supports static methods in one direction (Java/ObjC -> C++). You can either make a call from Java to provide the object for C++ to store and use later, or you can do the reverse and use the static method as a factory, letting Java ask C++ to create an object. The former is simpler if you don't mind using global state, or you need to use the object immediately.

interface example_so_setup +c {
    set_example_so(obj: example_so)
}

There's an example in the Djinni test suite , where test_helper is an interface with a method check_client_interface_ascii which is called from Java here . Java passes aJava object as an argument, which C++ then makes calls on.

If you want to avoid using global state, you can just add an extra step, where Java first calls a static factory method to create some sort of C++ "manager" object, then makes a call on that object to pass the example_so for callbacks. The specifics of how that would happen probably depend on your app's initialization needs.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM