ダウンコールハンドル
ネイティブ関数を呼び出します。
「ダウンコールハンドル」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。
ネイティブ関数の呼び出し
ダウンコールとは、Javaからネイティブコードへ下って呼び出すことです。FFMでは、各ネイティブ関数をLinkerから構築したMethodHandleとして表します。
このレッスンでは、C関数の検索、記述、呼び出しの手順を説明します。
ネイティブリンカー
Linker.nativeLinker()は、実行中のプラットフォーム用のリンカーを返します。Cの呼び出し規約を把握しており、ネイティブ関数へのハンドルを生成できます。
import java.lang.foreign.Linker;
public class Main {
public static void main(String[] args) {
Linker linker = Linker.nativeLinker();
System.out.println("Got native linker: " + linker);
}
}シンボルの検索
関数のアドレスが必要です。linker.defaultLookup()は標準Cライブラリを検索し、SymbolLookup.libraryLookup(name, arena)は指定した共有ライブラリを読み込みます。
import java.lang.foreign.*;
public class Main {
public static void main(String[] args) {
Linker linker = Linker.nativeLinker();
SymbolLookup stdlib = linker.defaultLookup();
MemorySegment addr = stdlib.find("strlen").orElseThrow();
System.out.println("strlen address found: " + addr);
}
}シグネチャの記述
FunctionDescriptorは、レイアウトを使ってCのシグネチャを記述します。size_t strlen(const char*)の場合、戻り値はlongで、引数はポインター(ADDRESS)です。
import java.lang.foreign.*;
public class Main {
public static void main(String[] args) {
FunctionDescriptor desc = FunctionDescriptor.of(
ValueLayout.JAVA_LONG, // return: size_t
ValueLayout.ADDRESS); // arg: const char*
System.out.println("Descriptor: " + desc);
}
}ハンドルの構築
シンボルとディスクリプターをdowncallHandleで組み合わせると、Javaメソッドと同じように呼び出せるMethodHandleを取得できます。
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
public class Main {
public static void main(String[] args) {
Linker linker = Linker.nativeLinker();
MethodHandle strlen = linker.downcallHandle(
linker.defaultLookup().find("strlen").orElseThrow(),
FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS));
System.out.println("Handle ready: " + strlen.type());
}
}関数の呼び出し
strlenを呼び出すには、ネイティブ文字列へのポインターを渡します。Arenaに文字列を割り当ててから、ハンドルをinvokeします。結果はJavaのlongとして返されます。
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
public class Main {
public static void main(String[] args) throws Throwable {
Linker linker = Linker.nativeLinker();
MethodHandle strlen = linker.downcallHandle(
linker.defaultLookup().find("strlen").orElseThrow(),
FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS));
try (Arena arena = Arena.ofConfined()) {
MemorySegment str = arena.allocateUtf8String("Hello, FFM");
long len = (long) strlen.invoke(str);
System.out.println("Length = " + len);
}
}
}型のマッピング
各C型は次のようにレイアウトへ対応付けられます。
int:ValueLayout.JAVA_INTlong/size_t:JAVA_LONGdouble:JAVA_DOUBLE- すべてのポインター:
ADDRESS
対応付けを正しく行うことが重要です。間違えると、呼び出しによってデータが破損します。
ポインターを返す関数
C関数がポインターを返す場合、ハンドルは長さ0のMemorySegmentを返します。先頭より後ろを読み取るには、reinterpret(byteSize)で正しいサイズに再解釈する必要があります。
多数の引数の受け渡し
FunctionDescriptor.of(returnLayout, arg1, arg2, ...)には、引数を順番に列挙します。void関数には戻り値のレイアウトを指定せず、FunctionDescriptor.ofVoid(args...)を使用します。
import java.lang.foreign.*;
public class Main {
public static void main(String[] args) {
// int (*)(const char*, int)
FunctionDescriptor two = FunctionDescriptor.of(
ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.JAVA_INT);
// void (*)(int)
FunctionDescriptor noReturn = FunctionDescriptor.ofVoid(ValueLayout.JAVA_INT);
System.out.println(two + " / " + noReturn);
}
}throwsでエラーを処理する
MethodHandle.invokeはthrows Throwableを宣言しているため、呼び出し元のメソッドではこれを処理するか、宣言する必要があります。例でthrows Throwableを宣言しているのはこのためです。
ハンドルのキャッシュ
ダウンコールハンドルの構築には比較的コストがかかるため、一度だけ構築して再利用します。一般的なパターンは、クラスの初期化時にstatic finalフィールドへハンドルを設定し、その後何度も呼び出す方法です。
ハンドル自体は、複数スレッドから同時に呼び出しても安全です。
確認問題
Cのポインター引数をどのように記述するか思い出してください。
まとめ
ネイティブ関数の呼び出し方を学びました。
Linker.nativeLinker()とSymbolLookupを使ってシンボルを検索しますFunctionDescriptorがレイアウトを使ってシグネチャを記述しますdowncallHandleによってinvokeできるMethodHandleを取得します- ポインターは
ADDRESSに対応し、invokeはThrowableをスローします
次は、レイアウトと構造体を使って複雑なネイティブデータを記述します。
よくある質問
「ダウンコールハンドル」レッスンは無料ですか?
はい。「ダウンコールハンドル」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。
「ダウンコールハンドル」で何を学びますか?
ネイティブ関数を呼び出します。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Java Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「ダウンコールハンドル」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このJava Academyレッスンでコードを書いて実行できますか?
はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。