JNR (Java Native Runtime) Mechanism for calling Native code from Java Main content of Project Panama JEP 191: Foreign Function Interface has been proposed
FFI (Foreign Function Interface) Interface is defined in Java ~~ (not) ~~, excellent thing that can be implemented in C or C ++ Create and call an object from a dynamic link library (.dll or .so) using a guy called LibraryLoader
Specify JDK version as 12 (not sure if it makes sense)
Add the library to dependencies in build.gradle.
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
//Additional part
dependencies {
//FFI body
compile group: 'com.github.jnr', name: 'jnr-ffi', version: '2.1.9'
//Libraries on which FFI depends
compile group: 'org.ow2.asm', name: 'asm', version: '7.0'
}
Since the library created in C is called, the specification is defined in Interface. The method definition in Interface is aligned with the function declaration in the header file (.h).
This time, we implemented Euclidean algorithm to find the greatest common divisor.
Describe the same contents as euclid.h in Interface.
fact.java
public interface test {
int euclid(int m, int n);
}
Create a Java file for the caller.
Test.java
import jnr.ffi.LibraryLoader;
public class Test {
public static void main(String[] args) {
System.out.println(
/*Load an object of type fact
In create, pass the class of the specified Interface
In the load argument, the filename of the dll file as a string(Excluding the extension)give*/
LibraryLoader.create(test.class).load("test")
//Run euclid
.euclid(2,4)
);
}
}
euclid.h
int euclid(int i, int j);
euclid.c
int euclid(int m, int n){
int tmp, r;
if(n > m){
tmp = m;
m = n;
n = tmp;
}
r = m % n;
if(r == 0) return n;
return euclid(n, r);
}
Yes, I usually implemented header files and programs in C.
Foreign Function Interface uses the dynamic link library, so it generates a .dll file. When creating a dll, let's create one that matches the number of bits of the OS. By the way, if you execute it in a state where the OS and the number of bits are different, or there is no dll in the first place, java.lang.UnsatisfiedLinkError: unknown I get the error.
I used gcc from MinGW.
gcc -shared -o test.dll test.cpp
You need to pass the path to the system properties java.library.path in order to include the .dll file you created in your project. In Java, you can set system properties as follows when executing java commands.
java -D<Property name>=<Property>
In this case, you can include the .dll file by executing it like this.
java -Djava.library.path=C:\\test.dll Test
When you run it in Gradle, you don't bother to type java commands, right? So, by adding a little to build.gradle, we will be able to pass system properties when running with the run button.
//Is the task name arbitrary?
task launch(type: JavaExec) {
//Specify the main class name for which you want to specify systemProperty
main = "Test"
//This is magic
classpath sourceSets.main.runtimeClasspath
systemProperty "java.library.path", "<.dll file path>"
}
jextract A tool that automatically generates a jar (with Interface) from a .h file
jextract is currently only included in the Early Access version of Project Panama. The trouble is that currently (January 14, 2019) only Linux and Mac versions are available. I can't help it, so I Introduced Bash on Ubuntu on Windows.
I usually got the binary with wget. It seems that make install etc. is ** unnecessary **. You can use it immediately by decompressing it with the tar command. By the way, let's pass the path.
wget https://download.java.net/java/early_access/panama/archive/0/binaries/jdk-12-foreign+0_linux-x64_bin.tar.gz
Just specify the .h file.
jextract test.h
Add the generated jar file (test.h.jar in this case) to your project. (Specify in the library with Edit-> Project Structure etc.)
Now you don't have to write the same description in Interface and header one by one.
So, I didn't like to put the file name in the property one by one when putting this jar and dll, and I didn't know how to set the system property when I put the dll in the jar, so instead I made a guy to do it.
The program is kept on github, so if you want to use it, pick it up and use it. Javakky/UseC4ffi4Windows
Place the dll in the resource folder. Simply pass the .dll file path (below / resource) and the Interface class to loadDll and the object will be returned.
LoadDllTest.java
public class LoadDllTest {
public static void main(String[] args) {
//You can get an object with just this
IClassA func = LoadDll.loadDll("<.dll name>", <interface>.class));
}
}
I published it in bintry and made it available from the gradle project.
build.gradle
repositories {
maven {
url 'https://dl.bintray.com/javakky/maven'
}
}
dependencies{
compile group: 'com.github.javakky', name: 'jnr-load-dill', version: '1.0.1'
}
・ Links for articles related to jnr-ffi
Recommended Posts