jextractを使用したネイティブ関数のコール
jextractツールは、ネイティブ・ライブラリのヘッダー・ファイルからJavaバインディングを機械的に生成します。このツールが生成するバインディングは、外部関数およびメモリー(FFM) APIに依存します。このツールを使用すると、呼び出す関数のダウンコール・ハンドルおよびアップコール・ハンドルを作成する必要はありません。jextract
ツールによって、そのためのコードが生成されます。
次のサイトからツールを入手します:
https://jdk.java.net/jextract/
jextractのソース・コードは次のサイトで入手します:
https://github.com/openjdk/jextract
このサイトには、jextractをコンパイルおよび実行する方法のステップ、その他のドキュメントおよびサンプルも含まれています。
JavaアプリケーションでのPythonスクリプトの実行
次のステップでは、Pythonヘッダー・ファイルPython.h
からJavaバインディングを生成し、生成されたコードを使用してJavaアプリケーションでPythonスクリプトを実行する方法を示します。このPythonスクリプトは、Java文字列の長さを出力します。
- 次のコマンドを実行して、
Python.h
のJavaバインディングを生成します:jextract -l <absolute path of Python shared library> \ --output classes \ -I <directory containing Python header files> \ -t org.python <absolute path of Python.h>
たとえば:
jextract -l /lib64/libpython3.6m.so.1.0 \ --output classes \ -I /usr/include/python3.6m \ -t org.python /usr/include/python3.6m/Python.h
ヒント:
-
Linuxシステムで、Python共有ライブラリのファイル名を取得するには、次のコマンドを実行します。この例では、システムにPython 3がインストールされていることを前提としています。
ldd $(which python3)
このコマンドを実行すると、次のような出力が表示されます:
linux-vdso.so.1 => (0x00007ffdb4bd5000) libpython3.6m.so.1.0 => /lib64/libpython3.6m.so.1.0 (0x00007fb0386a7000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fb03848b000) libdl.so.2 => /lib64/libdl.so.2 (0x00007fb038287000) libutil.so.1 => /lib64/libutil.so.1 (0x00007fb038084000) libm.so.6 => /lib64/libm.so.6 (0x00007fb037d82000) libc.so.6 => /lib64/libc.so.6 (0x00007fb0379b4000) /lib64/ld-linux-x86-64.so.2 (0x00007fb038bce000)
-
Linuxシステムで、
Python.h
またはPythonヘッダー・ファイルを含むディレクトリが見つからない場合は、python-devel
パッケージをインストールする必要がある場合があります。 jextract
ツールによって作成されるクラスおよびメソッドを調べる場合は、--source
オプションを指定してコマンドを実行します。たとえば、次のコマンドによって、Python.h
のJavaバインディングのソース・ファイルが生成されます:jextract --source \ --output src \ -I <directory containing Python header files> \ -t org.python <absolute path of Python.h>
-
classes
と同じディレクトリ(Python Javaバインディングが含まれる)に、次のファイルPythonMain.java
を作成します:import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import static java.lang.foreign.MemorySegment.NULL; import static org.python.Python_h.*; public class PythonMain { public static void main(String[] args) { String myString = "Hello world!"; String script = """ string = "%s" print(string, ': ', len(string), sep='') """.formatted(myString).stripIndent(); Py_Initialize(); try (Arena arena = Arena.ofConfined()) { MemorySegment nativeString = arena.allocateUtf8String(script); PyRun_SimpleStringFlags( nativeString, NULL); Py_Finalize(); } Py_Exit(0); } }
- 次のコマンドを使用して、
PythonMain.java
をコンパイルします:javac --enable-preview -source 21 -cp classes PythonMain.java
- 次のコマンドを使用して、
PythonMain
を実行します:java --enable-preview -cp classes:. --enable-native-access=ALL-UNNAMED PythonMain
Javaアプリケーションからのqsort関数のコール
前述のように、qsortは、2つの要素を比較する関数へのポインタを必要とするCライブラリ関数です。次のステップでは、jextract
を使用してC標準ライブラリのJavaバインディングを作成し、qsortで必要な比較関数のアップコール・ハンドルを作成し、qsort関数をコールします。
- 次のコマンドを実行して、C標準ライブラリのヘッダー・ファイルである
stdlib.h
のJavaバインディングを作成します:jextract --output classes -t org.unix <absolute path to stdlib.h>
たとえば:
jextract --output classes -t org.unix /usr/include/stdlib.h
stdlib.h
に対して生成されるJavaバインディングに含まれるのは、stdlib_h
という名前のJavaクラス(qsort(MemorySegment, long, long, MemorySegment)
という名前のJavaメソッドが含まれる)と、__compar_fn_tという名前のJavaインタフェース(allocateという名前のメソッドが含まれる)です。このメソッドによって、qsort
関数で必要な比較関数の関数ポインタが作成されます。jextract
によって生成されるJavaバインディングのソース・コードを調べるには、--sourceオプションを指定してツールを実行します:jextract --source --output src -t org.unix <absolute path to stdlib.h>
stdlib.h
のJavaバインディングを生成したディレクトリと同じディレクトリに、次のJavaソース・ファイルQsortMain.javaを作成します:import static org.unix.__compar_fn_t.*; import static org.unix.stdlib_h.*; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; public class QsortMain { public static void main(String[] args) { int[] unsortedArray = new int[] { 0, 9, 3, 4, 6, 5, 1, 8, 2, 7 }; try (Arena a = Arena.ofConfined()) { // Allocate off-heap memory and store unsortedArray in it MemorySegment array = a.allocateArray( ValueLayout.JAVA_INT, unsortedArray); // Create upcall for comparison function // // MemorySegment allocate(__compar_fn_t, Arena) is from // __compar_fn-t.java, generated by jextract MemorySegment comparFunc = allocate( (addr1, addr2) -> Integer.compare( addr1.get(ValueLayout.JAVA_INT, 0), addr2.get(ValueLayout.JAVA_INT, 0)), a); // Call qsort qsort(array, (long) unsortedArray.length, ValueLayout.JAVA_INT.byteSize(), comparFunc); // Deference off-heap memory int[] sortedArray = array.toArray(ValueLayout.JAVA_INT); for (int num : sortedArray) { System.out.print(num + " "); } System.out.println(); } } }
次の文によって、ラムダ式からアップコール
comparFunc
が作成されます:// Create upcall for comparison function // // MemorySegment allocate(__compar_fn_t, SegmentScope) is from // __compar_fn-t.java, generated by jextract MemorySegment comparFunc = allocate( (addr1, addr2) -> Integer.compare( addr1.get(ValueLayout.JAVA_INT, 0), addr2.get(ValueLayout.JAVA_INT, 0)), a);
したがって、アップコール: Javaコードを関数ポインタとして外部関数に渡すで説明されているように、比較関数のメソッド・ハンドルを作成する必要はありません。
- 次のコマンドを使用して、
QsortMain.java
をコンパイルします:javac --enable-preview -source 21 -cp classes QsortMain.java
- 次のコマンドを使用して、
QsortMain
を実行します:java --enable-preview -cp classes:. --enable-native-access=ALL-UNNAMED QsortMain