8

次のようなヘッダーを指定します。

#include <iostream>
#include <algorithm>
#include <iterator>

inline void foo(const signed char *arr, size_t sz) {
  std::copy_n(arr, sz, std::ostream_iterator<int>(std::cout, "\n"));
}

inline void bar(const signed char *begin, const signed char *end) {
  std::copy(begin, end, std::ostream_iterator<int>(std::cout, "\n"));
}

(ここでは便宜上 C++11 を使用しましたが、実装を変更した場合、これは C または C++ のいずれかになります)

これらの関数をラップして、Java 側で配列だけを取得し、配列の (既知の) サイズを使用してこれらの関数の 2 番目のパラメーターを提供するにはどうすればよいですか?

4

1 に答える 1

12

この重要な点は、これらの関数のいずれかをラップするには、複数引数の 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配列の基礎となるストレージへのポインターを要求することで、それを設定します(実際にはコピーである場合とそうでない場合があります)。$22 番目の 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計算するためにローカル変数を使用することです。endbegin

残された唯一のことは、今書いたタイプマップを使用して、ヘッダー ファイル自体をラップするように 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、これには別の欠点がありますfoobar特定の関数 - 2 つの範囲または 2 つのポインターと長さの組み合わせを取る関数がある場合、同じ関数で複数回適用されても、一致する場所ならどこでも適用されます。このようにすることの利点の 1 つは、他のラップされた関数がSWIGTYPE_p_signed_char戻ってきた場合でも、必要に応じてオーバーロードを使用できることです。ByteArrayfrom がある場合%array_classでも、生成に必要なJavaでポインター演算を実行できませんendあなたのために。

示されている元の方法は、Java でよりクリーンなインターフェイスを提供し、過剰なコピーを作成せず、より再利用可能であるという利点が追加されています。


ラッピングのもう 1 つの代替アプローチは、 andの%inlineオーバーロードをいくつか記述することです。foobar

%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 の方がはるかに優れているように見えます。

于 2012-07-20T18:00:51.897 に答える