Skip to content

[Pattern] #129

Description

@IanDarwin

Category

language

Slug

call-c-from-java

Title

Calling out to C code from Java

Difficulty

advanced

Since JDK

22

Summary

Simplifies C/C++ code to be called from Java, allowing most existing libraries to be used without adapters or cumbersome C code modification.

Old code label

Java 1.1+

Old code

publicclassCallCFromJava {
static { System.loadLibrary("hello-jni"); }
publicstaticnativeintgreet(Stringname);
publicstaticvoidmain(String[] args) {
intret = greet("Bambi");
System.out.println("Return value " + ret);
}
}
Thenrunjavac -h, thenviewthegenerated .hfile, thenwriteyour .cfile:
#include"CallCFromJava.h"JNIEXPORTintJNICALLJava_CallCFromJava_greet(JNIEnv *env, jclassthis, jstringstr) {
constchar* name = (*env)->GetStringUTFChars(env, str, NULL);
printf("Hello %s\n", name);
return0;
}

Modern code label

Java 22+

Modern code

publicclassCallCFromJava {
voidmain() throwsThrowable {
try (Arenaarena = Arena.ofConfined()) {
SymbolLookupourLib = SymbolLookup.libraryLookup("libhello-ffi.so", arena);
Optional<MemorySegment> segment = ourLib.find("greet");
MemorySegmentforeignFuncAddr = segment.get();
FunctionDescriptorgreet_sig= // return and input parameterFunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS);
MethodHandlegreetMethod =
Linker.nativeLinker().downcallHandle(foreignFuncAddr, greet_sig);
varret = (long) greetMethod.invokeExact(arena.allocateFrom("Bambi"));
System.out.println("Return value " + ret);
}
}
}
CCode:
intgreet(char* name) {
printf("Hello %s\n", name);
return0;
}

Explanation

Java has two techniques for interfacing between Java and C/C++ code, the traditional JNI and the newer FFM. With JNI you simply declare your method as native, but then you have to run javac -h to generate a C-language .h (header) file, then have your C code implement that, and deal with the slightly cumbersome JNI C-language API.

FFM simplifies the C code to the point that it doesn't have to know anything about Java. This makes it much easier to interface with existing C/C++-language libraries. It does take a few more lines of Java code, but the net effect is more readable and more maintainable. Compare the C code in both versions and you'll see the difference!

Minimal runnable demo can be found at https://github.com/IanDarwin/javaevolved-java_callling_c

Why the modern way wins

👁 Better readability — C code doesn't have to learn Java!
⚡ More flexible - can directly call most existing C/C++ libraries
⚡ Easier - don't have to stop, run javac -h, and implement the "interface" defined in .h file.

Metadata

Metadata

Labels

slugNew or updated pattern snippet (category/slug.json)

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions