この重要な点は、これらの関数のいずれかをラップするには、複数引数の typemapを使用する必要があるということです。
プリアンブルは、SWIG のかなり標準的なものです。個人的にお気に入りの prgama を使用して、インターフェイスのユーザーが知る必要なく共有ライブラリを自動的にロードしました。
%module test
%{
#include "test.hh"
%}
%pragma(java) jniclasscode=%{
static {
try {
System.loadLibrary("test");
} catch (UnsatisfiedLinkError e) {
System.err.println("Native code library failed to load. \n" + e);
System.exit(1);
}
}
%}
最初に、Java インターフェースの両方の部分 (JNI とそれを呼び出すラッパー) のタイプとして使用するように SWIG に指示するために、いくつかのJava タイプマップを使用する必要があります。byte[]
生成モジュール ファイルでは、JNI タイプを使用しますjbyteArray
。入力を SWIG インターフェースから、それが生成する JNI に直接渡します。
%typemap(jtype) (const signed char *arr, size_t sz) "byte[]"
%typemap(jstype) (const signed char *arr, size_t sz) "byte[]"
%typemap(jni) (const signed char *arr, size_t sz) "jbyteArray"
%typemap(javain) (const signed char *arr, size_t sz) "$javainput"
これが完了すると、複数引数の型マップを書くことができます:
%typemap(in,numinputs=1) (const signed char *arr, size_t sz) {
$1 = JCALL2(GetByteArrayElements, jenv, $input, NULL);
$2 = JCALL1(GetArrayLength, jenv, $input);
}
in typemap の仕事は、JNI 呼び出しによって与えられたものから、実際の関数が実際に入力として期待するものに変換することです。以前numinputs=1
は、2 つの実際の関数引数が Java 側で 1 つの入力のみを受け取ることを示していましたが、いずれにせよこれはデフォルト値であるため、明示的に述べる必要はありません。
この typemap$1
は typemap の最初の引数、つまりこの場合は関数の最初の引数です。Java配列の基礎となるストレージへのポインターを要求することで、それを設定します(実際にはコピーである場合とそうでない場合があります)。$2
2 番目の typemap 引数を配列のサイズに設定します。
ここのJCALLn
マクロは、typemap が C と C++ JNI の両方でコンパイルできることを確認します。言語の適切な呼び出しに展開されます。
実際の関数呼び出しが返されたら、クリーンアップするために別のタイプマップが必要です。
%typemap(freearg) (const signed char *arr, size_t sz) {
// Or use 0 instead of ABORT to keep changes if it was a copy
JCALL3(ReleaseByteArrayElements, jenv, $input, $1, JNI_ABORT);
}
これはReleaseByteArrayElements
、配列の処理が完了したことを JVM に伝えるために呼び出します。ポインターと、それを取得した Java 配列オブジェクトが必要です。さらに、コンテンツが変更され、最初に取得したポインターがコピーであった場合に、コンテンツをコピーする必要があるかどうかを示すパラメーターを取ります。jboolean
(NULL を渡した引数は、コピーが与えられたかどうかを示す、へのオプションのポインターです)。
2 番目のバリアントのタイプマップはほぼ同じです。
%typemap(in,numinputs=1) (const signed char *begin, const signed char *end) {
$1 = JCALL2(GetByteArrayElements, jenv, $input, NULL);
const size_t sz = JCALL1(GetArrayLength, jenv, $input);
$2 = $1 + sz;
}
%typemap(freearg) (const signed char *begin, const signed char *end) {
// Or use 0 instead of ABORT to keep changes if it was a copy
JCALL3(ReleaseByteArrayElements, jenv, $input, $1, JNI_ABORT);
}
%typemap(jtype) (const signed char *begin, const signed char *end) "byte[]"
%typemap(jstype) (const signed char *begin, const signed char *end) "byte[]"
%typemap(jni) (const signed char *begin, const signed char *end) "jbyteArray"
%typemap(javain) (const signed char *begin, const signed char *end) "$javainput"
唯一の違いは、ポインターを使用して引数をsz
計算するためにローカル変数を使用することです。end
begin
残された唯一のことは、今書いたタイプマップを使用して、ヘッダー ファイル自体をラップするように SWIG に指示することです。
%include "test.hh"
これらの機能を次のようにテストしました。
public class run {
public static void main(String[] argv) {
byte[] arr = {0,1,2,3,4,5,6,7};
System.out.println("Foo:");
test.foo(arr);
System.out.println("Bar:");
test.bar(arr);
}
}
これは期待どおりに機能しました。
便宜上、これを書く際に使用したファイルを自分のサイトで共有しました。この回答を順番にたどることで、そのアーカイブ内のすべてのファイルのすべての行を再構築できます。
参考までに、JNI 呼び出しを使用せずに、%pragma(java) modulecode
(純粋な Java での) 入力を実際の関数が期待する形式に変換するオーバーロードを生成するために使用することで、すべてを実行できたはずです。そのため、モジュール ファイルは次のようになります。
%module test
%{
#include "test.hh"
%}
%include <carrays.i>
%array_class(signed char, ByteArray);
%pragma(java) modulecode = %{
// Overload foo to take an array and do a copy for us:
public static void foo(byte[] array) {
ByteArray temp = new ByteArray(array.length);
for (int i = 0; i < array.length; ++i) {
temp.setitem(i, array[i]);
}
foo(temp.cast(), array.length);
// if foo can modify the input array we'll need to copy back to:
for (int i = 0; i < array.length; ++i) {
array[i] = temp.getitem(i);
}
}
// How do we even get a SWIGTYPE_p_signed_char for end for bar?
public static void bar(byte[] array) {
ByteArray temp = new ByteArray(array.length);
for (int i = 0; i < array.length; ++i) {
temp.setitem(i, array[i]);
}
bar(temp.cast(), make_end_ptr(temp.cast(), array.length));
// if bar can modify the input array we'll need to copy back to:
for (int i = 0; i < array.length; ++i) {
array[i] = temp.getitem(i);
}
}
%}
// Private helper to make the 'end' pointer that bar expects
%javamethodmodifiers make_end_ptr "private";
%inline {
signed char *make_end_ptr(signed char *begin, int sz) {
return begin+sz;
}
}
%include "test.hh"
%pragma(java) jniclasscode=%{
static {
try {
System.loadLibrary("test");
} catch (UnsatisfiedLinkError e) {
System.err.println("Native code library failed to load. \n" + e);
System.exit(1);
}
}
%}
データを正しい型にするために必要な明らかな (2 つの) コピーに加えて ( から に移動する簡単な方法はありませんbyte[]
) SWIGTYPE_p_signed_char
、これには別の欠点がありますfoo
。bar
特定の関数 - 2 つの範囲または 2 つのポインターと長さの組み合わせを取る関数がある場合、同じ関数で複数回適用されても、一致する場所ならどこでも適用されます。このようにすることの利点の 1 つは、他のラップされた関数がSWIGTYPE_p_signed_char
戻ってきた場合でも、必要に応じてオーバーロードを使用できることです。ByteArray
from がある場合%array_class
でも、生成に必要なJavaでポインター演算を実行できませんend
あなたのために。
示されている元の方法は、Java でよりクリーンなインターフェイスを提供し、過剰なコピーを作成せず、より再利用可能であるという利点が追加されています。
ラッピングのもう 1 つの代替アプローチは、 andの%inline
オーバーロードをいくつか記述することです。foo
bar
%inline {
void foo(jbyteArray arr) {
// take arr and call JNI to convert for foo
}
void bar(jbyteArray arr) {
// ditto for bar
}
}
これらは Java インターフェイスのオーバーロードとして提示されますが、それでもモジュール固有であり、さらにここで必要な JNI は、他の方法よりも複雑ですjenv
。デフォルト。オプションは、それを取得するための遅い呼び出し、またはnuminputs=0
パラメーターを自動的に埋める typemap です。いずれにせよ、複数引数の typemap の方がはるかに優れているように見えます。