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 <directory containing code generated by jextract> \ -I <directory containing Python header files> \ -t org.python <absolute path of Python.h>
たとえば:
jextract -l :/lib64/libpython3.6m.so.1.0 \ --output gensrc \ -I /usr/include/python3.6m \ -t org.python /usr/include/python3.6m/Python.h
Linuxで、Python共有ライブラリのパスを取得するには、次のコマンドを実行します:
ldconfig -p | grep libpython
このコマンドを実行すると、次のような出力が表示されます:
libpython3.6m.so.1.0 (libc6,x86-64) => /lib64/libpython3.6m.so.1.0 libpython3.6m.so (libc6,x86-64) => /lib64/libpython3.6m.so libpython3.so (libc6,x86-64) => /lib64/libpython3.so libpython2.7.so.1.0 (libc6,x86-64) => /lib64/libpython2.7.so.1.0 libpython2.7.so (libc6,x86-64) => /lib64/libpython2.7.so
-l
オプションの値は、生成されたヘッダー・クラスがロードする必要のある共有ライブラリのパスまたは名前です。コロン(:
)で始まる場合、値はライブラリ・パスとして解釈されます。それ以外の場合は、libGL.so
のGL
などのライブラリ名です。jextract
ツールは、動的リンカーによって認識される任意のライブラリ指定子を解決できます。したがって、このコマンドは次のように実行できます。jextract -l :libpython3.6m.so.1.0 \ --output gensrc \ -I /usr/include/python3.6m \ -t org.python /usr/include/python3.6m/Python.h
Linuxシステムで、
Python.h
またはPythonヘッダー・ファイルを含むディレクトリが見つからない場合は、python-devel
パッケージをインストールする必要がある場合があります。 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.allocateFrom(script); PyRun_SimpleStringFlags( nativeString, NULL); Py_Finalize(); } Py_Exit(0); } }
- 次のコマンドを使用して、
PythonMain.java
をコンパイルします:javac -sourcepath gensrc PythonMain.java
- 次のコマンドを使用して、
PythonMain
を実行します:java -cp gensrc:. --enable-native-access=ALL-UNNAMED PythonMain
Javaアプリケーションからのqsort関数のコール
前述のように、qsortは、2つの要素を比較する関数へのポインタを必要とするCライブラリ関数です。次のステップでは、jextract
を使用してC標準ライブラリのJavaバインディングを作成し、qsortで必要な比較関数のアップコール・ハンドルを作成し、qsort関数をコールします。
- 次のコマンドを実行して、C標準ライブラリのヘッダー・ファイルである
stdlib.h
のJavaバインディングを作成します:jextract --output <directory containing code generated by jextract> \ -t org.unix <absolute path to stdlib.h>
たとえば:
jextract --output gensrc -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
関数で必要な比較関数の関数ポインタが作成されます。 stdlib.h
のJavaバインディングを生成したディレクトリと同じディレクトリに、次のJavaソース・ファイルQsortMain.javaを作成します: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 // // stdlib_h.C_INT is a constant generated by jextract MemorySegment array = a.allocateFrom(C_INT, unsortedArray); // Create upcall stub for the comparison function // // MemorySegment org.unix.__compar_fn_t.allocate(__compar_fn_t, Arena) // is from __compar_fn_t.java, generated by jextract MemorySegment comparFunc = org.unix.__compar_fn_t.allocate( (addr1, addr2) -> Integer.compare( addr1.get(C_INT, 0), addr2.get(C_INT, 0)), a); // Call qsort qsort(array, (long) unsortedArray.length, C_INT.byteSize(), comparFunc); // Copy off-heap memory into new int[] int[] sortedArray = array.toArray(C_INT); for (int num : sortedArray) { System.out.print(num + " "); } System.out.println(); } } }
次の文によって、ラムダ式からアップコール
comparFunc
が作成されます:// Create upcall for comparison function // // MemorySegment org.unix.__compar_fn_t.allocate(__compar_fn_t, SegmentScope) // is from __compar_fn-t.java, generated by jextract MemorySegment comparFunc = org.unix.__compar_fn_t.allocate( (addr1, addr2) -> Integer.compare( addr1.get(C_INT, 0), addr2.get(C_INT, 0)), a);
したがって、アップコール: Javaコードを関数ポインタとして外部関数に渡すで説明されているように、比較関数のメソッド・ハンドルを作成する必要はありません。
- 次のコマンドを使用して、
QsortMain.java
をコンパイルします:javac -sourcepath gensrc QsortMain.java
- 次のコマンドを使用して、
QsortMain
を実行します:java -cp gensrc:. --enable-native-access=ALL-UNNAMED QsortMain