I would to know if there is a way to call a java method from cpp with a Functional Interface and use this callback inside my java code.
This is my code :
Main.java
import java.util.function.Consumer;
public class Main {
static {
System.loadLibrary("Main");
}
public static void main(String[] args) {
Main m = new Main();
m.nativeMethod();
}
public void test() {
System.out.println("test");
}
public void getCallback(Consumer<String> consumer) {
consumer.accept("Data from Java to cpp");
}
public native void nativeMethod();
}
Main.cpp
#include <iostream>
#include <string>
#include "Main.h"
extern "C"
{
JNIEXPORT void JNICALL myCallback(JNIEnv *env, jobject obj, jstring data)
{
jboolean isCopy;
const char *cData = env->GetStringUTFChars(data, &isCopy);
std::string cppData(cData);
env->ReleaseStringUTFChars(data, cData);
std::cout << cppData << '\n';
}
JNIEXPORT void JNICALL Java_Main_nativeMethod(JNIEnv *env, jobject obj)
{
std::cout << "Hello from Java_Main_nativeMethod" << '\n';
jmethodID jTest = env->GetMethodID(
env->GetObjectClass(obj),
"test",
"()V");
env->CallVoidMethod(obj, jTest);
jmethodID jFunction = env->GetMethodID(
env->GetObjectClass(obj),
"getCallback",
"(Ljava/util.function/Consumer;)V");
env->CallVoidMethod(obj, jFunction, myCallback);
}
}
compile.bash
#!/bin/bash
cd "$(dirname "$0")"
javac -h . Main.java # generates header
javac Main.java
gcc -lstdc++ -shared -I /Library/Java/JavaVirtualMachines/temurin-20.jdk/Contents/Home/include -o libMain.jnilib Main.cpp
java Main
output of $ bash compile.bash
Hello from Java_Main_nativeMethod
test
#
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x0000000108554b14, pid=72842, tid=10499
#
# JRE version: OpenJDK Runtime Environment Temurin-20.0.2+9 (20.0.2+9) (build 20.0.2+9)
# Java VM: OpenJDK 64-Bit Server VM Temurin-20.0.2+9 (20.0.2+9, mixed mode, tiered, compressed oops, compressed class ptrs, g1 gc, bsd-aarch64)
# Problematic frame:
# V [libjvm.dylib+0x514b14] jni_CallVoidMethodV+0xe0
#
# No core dump will be written. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#
# An error report file with more information is saved as:
# hs_err_pid72842.log
#
# If you would like to submit a bug report, please visit:
# https://github.com/adoptium/adoptium-support/issues
#
zsh: abort java Main
How can I make it work ?
Sure, it can be done, but it requires jumping through a few more hoops than you perhaps were hoping.
First, provide a concrete Java implementation on the
Consumerinterface that can hold some sort of reference to its C++ counterpart:Next, implement the native methods:
This is just meant as an illustrative proof of concept, so it lacks a lot of error checking that you ought to have in real-world JNI code.
And the way you'd use all this in your Java code would be something like: