JavaからC言語を呼び出す

# 概要

JNAを使ってC言語を呼び出す方法をまとめました。

# 前準備

まず、JNAのライブラリをmavenなりbuild.sbtなりに追加します。

```build.sbt
scalaVersion := "2.13.6"
libraryDependencies += "net.java.dev.jna" % "jna" % "5.9.0"
libraryDependencies += "net.java.dev.jna" % "jna-platform" % "5.9.0"
```

# JavaからC言語を呼び出す

## Hello World的なプログラム

### C言語で共有ライブラリを作る

以下のようなプログラムを作成します。

```hoge.c
#include <stdio.h>

void hoge() {
  printf("ほげぇ\n");
}
```

そして、以下のコマンドで共有ライブラリを作成します。

```sh
gcc -dynamiclib -o libhoge.dylib hoge.c
```

### Java側の実装

Java側で以下のような実装をして、mainを実行すればOKです。

```java
import com.sun.jna.Library;
import com.sun.jna.Native;

public class JnaHoge {
    public interface HogeInterface extends Library {
        HogeInterface INSTANCE = Native.load("hoge", HogeInterface.class);

        void hoge();
    }

    public static void main(String[] args) {
        System.out.println("started");
        HogeInterface.INSTANCE.hoge();
        System.out.println("finished");
    }
}
```

### ディレクトリツリー

```
.
├── build.sbt
├── hoge.c
├── libhoge.dylib
├── src
    ├── main
    │   ├── java
    │   │   └── JnaHoge.java
    │   ├── resources
    │   └── scala
    └── test
        └── scala

```

### 実行結果

sbtの場合、`sbt "runMain JnaHoge"`で実行できます。

作成した`JnaHoge#main`を実行すると、
以下のどちらかのパターンで標準出力されるみたいです。

```
started
ほげぇ
finished
```

or

```
started
finished
ほげぇ
```

## C言語で共有ライブラリを作って、Javaから呼び出すサンプルコード

下記リンクには、JNAでのC言語とJavaの型の対応が記載されています。
このリンクに書かれている型以外に、配列や構造体をJavaとCの間で受け渡せるようです。

https://github.com/java-native-access/jna/blob/master/www/Mappings.md


### 引数なしでintの値を取得する

```hoge.c
#include <stdio.h>

int getOne() {
  return 1;
}
```

```JnaHoge.java
import com.sun.jna.Library;
import com.sun.jna.Native;

public class JnaHoge {
    public interface HogeInterface extends Library {
        HogeInterface INSTANCE = Native.load("hoge", HogeInterface.class);

        int getOne();
    }

    public static void main(String[] args) {
        System.out.println("started");
        int one = HogeInterface.INSTANCE.getOne();
        System.out.println(one);
        System.out.println("finished");
    }
}
```

`JnaHoge#main`の実行結果:

```
started
1
finished
```

### int型を渡してint型の値を得る

```hoge.c
#include <stdio.h>

int squared(int x) {
  return x * x;
}
```

```JnaHoge.java
import com.sun.jna.Library;
import com.sun.jna.Native;

public class JnaHoge {
    public interface HogeInterface extends Library {
        HogeInterface INSTANCE = Native.load("hoge", HogeInterface.class);

        int squared(int x);
    }

    public static void main(String[] args) {
        System.out.println("started");
        int squared = HogeInterface.INSTANCE.squared(4);
        System.out.println(squared);
        System.out.println("finished");
    }
}
```

実行結果:

```
started
16
finished
```

### String型を渡してString型の値を得る

```hoge.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char* appendHoge(char* chars) {
  char hoge[] = "hoge";
  char* newChars = malloc(sizeof(chars) + sizeof(hoge));

  memcpy(newChars, chars, sizeof(chars));
  strcat(newChars, hoge);

  return newChars;
}
```

```JnaHoge.java
import com.sun.jna.Library;
import com.sun.jna.Native;

public class JnaHoge {
    public interface HogeInterface extends Library {
        HogeInterface INSTANCE = Native.load("hoge", HogeInterface.class);

        String appendHoge(String cs);
    }

    public static void main(String[] args) {
        System.out.println("started");
        String str = HogeInterface.INSTANCE.appendHoge("xxx");
        System.out.println(str);
        System.out.println("finished");
    }
}
```

実行結果:

```
started
xxxhoge
finished
```

### 配列を渡して配列を得る

```hoge.c
#include <stdio.h>

// 引数resultを戻り値として扱う
void hoge(int* nums, int* result) {
    result[0] = nums[0] * 10;
    result[1] = nums[1] * 10;
    result[2] = nums[2] * 10;
}
```

```JnaHoge.java
import com.sun.jna.Library;
import com.sun.jna.Native;

public class JnaHoge {
    public interface HogeInterface extends Library {
        HogeInterface INSTANCE = Native.load("hoge", HogeInterface.class);

        void hoge(int[] array, int[] newArray);
    }

    public static void main(String[] args) {
        System.out.println("started");

        int[] array = {4, 5, 6};
        int[] newArray = new int[3];
        HogeInterface.INSTANCE.hoge(array, newArray);

        System.out.println(newArray[0]);
        System.out.println(newArray[1]);
        System.out.println(newArray[2]);
        System.out.println("finished");
    }
}
```

実行結果:

```
started
40
50
60
finished
```

### 構造体を渡してJavaのクラスを得る

```hoge.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
  int age;
  char* name;
} PERSON;

PERSON* setName(PERSON *p, char* name) {
  p->name = name;
  return p;
}
```

```JnaHoge.java
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Structure;

public class JnaHoge {
    public interface HogeInterface extends Library {
        HogeInterface INSTANCE = Native.load("hoge", HogeInterface.class);

        @Structure.FieldOrder({"age", "name"})
        public class Person extends Structure {
            public int age;
            public String name;
        }

        Person setName(Person person, String name);
    }

    public static void main(String[] args) {
        System.out.println("started");
        HogeInterface.Person person = new HogeInterface.Person();
        HogeInterface.Person newPerson = HogeInterface.INSTANCE.setName(person, "パターソン");
        System.out.println(newPerson.name + "(" + newPerson.age + ")");
        System.out.println("finished");
    }
}
```

実行結果:

```
started
パターソン(0)
finished
```