[JAVA] I used jnr-ffi (made it easier to use, etc.)

Glossary

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

Try using FFI

Project creation

Specify JDK version as 12 (not sure if it makes sense)

Add library

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

Create Interface for function definition

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.

Create an Interface

Describe the same contents as euclid.h in Interface.

fact.java


public interface test {
    int euclid(int m, int n);
}

Create a Main class

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)
        );
    }
}

Implement the process in C

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.

Generate a .dll file

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

Specifying system properties

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

Specifying system properties in Gradle

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

Interface generation automation

jextract A tool that automatically generates a jar (with Interface) from a .h file

Supported OS

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.

Install Panama Early Access

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

Run jextract

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.

Main subject

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

How to use

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));
    }
}

Use with gradle

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

List of articles referred to

Links for articles related to jnr-ffi

Recommended Posts

I used jnr-ffi (made it easier to use, etc.)
I want to use FormObject well
I tried to summarize the methods used
I want to use DBViewer with Eclipse 2018-12! !!
I want to use @Autowired in Servlet
[Rails] Enum is easier to use! Enumelize!
I tried to use Selenium like JQuery
Since the du command used when the capacity is full is difficult to use, I tried wrapping it with ruby
When I tried to use a Wacom tablet with ubuntu 20.04, I didn't recognize it.
I want to use arrow notation in Ruby
I want to use java8 forEach with index
The code I used to connect Rails 3 to PostgreSQL 10
I made a Docker container to run Maven
[Java] I studied polymorphism, so I will summarize how to use it and its merits.