Skip to content

[Pattern] #129

@IanDarwin

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

public class CallCFromJava {
	static { System.loadLibrary("hello-jni"); }
	public static native int greet(String name);
	public static void main(String[] args) {
		int ret = greet("Bambi");
		System.out.println("Return value " + ret);
	}
}

Then run javac -h, then view the generated .h file, then write your .c file:

#include "CallCFromJava.h"

JNIEXPORT int JNICALL Java_CallCFromJava_greet(JNIEnv *env, jclass this, jstring str) {
	const char* name = (*env)->GetStringUTFChars(env, str, NULL);
	printf("Hello %s\n", name);
	return 0;
}

Modern code label

Java 22+

Modern code

public class CallCFromJava {
  void main() throws Throwable {

    try (Arena arena = Arena.ofConfined()) {
      SymbolLookup ourLib = 
        SymbolLookup.libraryLookup("libhello-ffi.so", arena);
      Optional<MemorySegment> segment = ourLib.find("greet");
      MemorySegment foreignFuncAddr = segment.get();
      FunctionDescriptor greet_sig= // return and input parameter
          FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS);
      MethodHandle greetMethod =
        Linker.nativeLinker().downcallHandle(foreignFuncAddr, greet_sig);
      var ret = (long) greetMethod.invokeExact(arena.allocateFrom("Bambi"));
      System.out.println("Return value " + ret);
    }
  }
}

C Code:
int greet(char* name) {
    printf("Hello %s\n", name);
    return 0;
}

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