5

C ライブラリ用に SWIG で生成された Java バインディングを開発しています。ライブラリには、 type のパラメータを取る関数が含まれていますvoid *。C 側では、これらは通常、型の配列へのポインタとして渡されるfloatか、またはintにキャストされvoid *ます。生成された Java バインディングでは、これにより type のパラメータを取るメソッドが生成されますSWIGTYPE_p_void

SWIGTYPE_p_voidこれらのメソッドに型として渡すことができるように、Javaバインディングでfloat/intの配列を構築する最良の方法は何ですか?

現在、example.i ファイルでヘルパー関数を定義しています。

void *floata_to_voidp(float f[])
{
    return (void *)f;
}

次に、Java 側で次のようなことを行います。

float foo[] = new float[2];
SWIGTYPE_p_void av = null;

// do something with foo

av = example.floata_to_voidp(foo);
example.myfunction(av);

特に、inta_to_voidp()サポートしたい型変換ごとに SWIG インターフェイス ファイルに etc が必要になるため、これはかなり醜いように思えます。

ヘルパー関数を使用せずに、Java 側でデータ型を変換するための余分なコードを少なくする方法はありますか?

更新 (17/6/12):質問に追加の詳細を与える: 私がやろうとしているのは、プロトタイプint foo(const float *data, int N, const void *argv, float *result)を使用して一連の C 関数を取得し、それらを任意の型の配列を使用できる Java 側のメソッドにマップすることですとして渡されargvます。argvであり、 ではconst void *ないことに注意してくださいvoid *

4

2 に答える 2

3

この答えには別の方法があります。それは非常に異なり、この問題に対してより自然な解決策を提供し、最初に探していたものに近づけます。他の提案は、オーバーロード (面倒、手動) を追加するか、何らかの方法でarray_classes に共通のインターフェイスを実装させることに焦点を当てていました。

それが見落としているのは、ほとんどの場合、これが Java にObject適しているということです。Java の配列でvoid*さえsです。これは、SWIG マップを持っている場合、渡したい配列を入力として受け入れることを意味します。少し注意して JNI を使用すると、その配列の先頭へのポインターを取得して、関数に渡すことができます。明らかに、例外を除いて非配列を拒否する必要があります。Objectvoid*ObjectObject

実際の基になるポインターの抽出を調整し、完了したらそれを解放するために、いくつかの (プライベート) ヘルパー関数を作成することになりますが、このソリューションの優れている点は、これを 1 回実行するだけで、次の型マップになることです。このように配列を取る任意の関数に使用できますvoid*

このソリューションの次の SWIG インターフェイスになりました。

%module test

%{
#include <stdint.h>

void foo(void *in) {
  printf("%p, %d, %g\n", in, *(jint*)in, *(jdouble*)in);
}
%}

%typemap(in,numinputs=0) JNIEnv *env "$1 = jenv;"

%javamethodmodifiers arr2voidd "private";
%javamethodmodifiers arr2voidi "private";
%javamethodmodifiers freearrd "private";
%javamethodmodifiers freearri "private";
%inline %{
jlong arr2voidd(JNIEnv *env, jdoubleArray arr) {
  void *ptr = (*env)->GetDoubleArrayElements(env, arr, NULL);
  return (intptr_t)ptr;
}

void freearrd(JNIEnv *env, jdoubleArray arr, jlong map) {
  void *ptr = 0;
  ptr = *(void **)&map;
  (*env)->ReleaseDoubleArrayElements(env, arr, ptr, JNI_ABORT);
}

jlong arr2voidi(JNIEnv *env, jintArray arr) {
  void *ptr = (*env)->GetIntArrayElements(env, arr, NULL);
  return (intptr_t)ptr;
}

void freearri(JNIEnv *env, jintArray arr, jlong map) {
  void *ptr = 0;
  ptr = *(void **)&map;
  (*env)->ReleaseIntArrayElements(env, arr, ptr, JNI_ABORT);
}
%}


%pragma(java) modulecode=%{
  private static long arrPtr(Object o) {
    if (o instanceof double[]) {
      return arr2voidd((double[])o);
    }
    else if (o instanceof int[]) {
      return arr2voidi((int[])o);
    }
    throw new IllegalArgumentException();
  }

  private static void freeArrPtr(Object o, long addr) {
    if (o instanceof double[]) {
      freearrd((double[])o, addr);
      return;
    }
    else if (o instanceof int[]) {
      freearri((int[])o, addr);
      return;
    }
    throw new IllegalArgumentException();
  }
%}

%typemap(jstype) void *arr "Object"
%typemap(javain,pre="    long tmp$javainput = arrPtr($javainput);",post="      freeArrPtr($javainput, tmp$javainput);") void *arr "tmp$javainput"

void foo(void *arr);

これは 2 つの配列型に対して実装されます。有限数は少なく、フラグメントやマクロを使用してこれを支援することもできます。内部的には、SWIG はjlongポインターを表すために a を使用します。したがって、配列の型ごとに、特定の配列のポインターを返す関数と、それを解放する別の関数が必要です。これらは非公開で、モジュール クラスの一部です。モジュール以外の誰も、これがどのように機能するかを知る必要はありません。

Object次に、 and を使用する2 つの関数がありinstanceof(醜いですが、Java の配列には他の共通ベースまたはインターフェイスがなく、ジェネリックは役に立ちません)、正しい関数を呼び出してポインターを取得/解放します。

これらを使用すると、SWIG をすべてのvoid *arr引数に使用するように設定するのは 2 つのタイプマップだけです。jstype タイプマップは、これらの場合に使用Objectするように SWIG に指示します。void*javain typemap は、一時的なローカル変数がポインターを ( 内にlong) 保持し、それを使用して呼び出しを行い、呼び出しが成功または失敗したときにクリーンアップされるように調整します。

于 2012-06-14T18:11:11.863 に答える
1

最も簡単な解決策は、SWIG を使用<carrays.i>floatして、配列と配列をラップする型を作成し、int関心のある他の型を作成することです。これらは、メンバー関数SWIGTYPE_p_floatを使用して簡単に etc.に変換できます。問題は、これが自動的にJava 内cast()の from に変換できないことです。SWIGTYPE_p_void理論的には、次のように呼び出すことができます。

new SWIGTYPE_p_void(FloatArray.getCPtr(myfloatarr));

しかし、さまざまな理由 (特に面倒) から、それは理想的とは言えません。(メモリの所有権とガベージ コレクションにも問題があります)。

代わりに、インターフェイスを定義しました。

public interface VoidPtr {
  public long asVoidPtr();
}

ライブラリのラップされたバージョンを入力として取り、配列クラスFloatArrayなどIntArrayを実装できること。

これはモジュールファイルで終わります:

%module test

%include <carrays.i>

%typemap(javainterfaces) FloatArray "VoidPtr"
%typemap(javainterfaces) IntArray "VoidPtr"

%typemap(javacode) FloatArray %{
  public long asVoidPtr() {
    return getCPtr(this);    
  }
%}

%typemap(javacode) IntArray %{
  public long asVoidPtr() {
    return getCPtr(this);
  }
%}

%array_class(float, FloatArray);
%array_class(int, IntArray);

%typemap(jstype) void *arr "VoidPtr"
%typemap(javain) void *arr "$javainput.asVoidPtr()"

void foo(void *arr);

void *arrタイプとして扱われるように変更しVoidPtr、メソッドを自動的に呼び出しますasVoidPtr()タイプマップのコピーまたはマクロを使用して、これの繰り返しを減らすことができます。(これをどのように使用するかによっては、ここで対処する必要がある時期尚早のガベージ コレクションに関する問題が発生する可能性があることに注意してください)

これにより、次のようなコードを書くことができます。

public class run {
  public static void main(String[] argv) {
    FloatArray arr = new FloatArray(100);
    test.foo(arr);    
  }
}

これが最も簡単でクリーンなソリューションだと思います。ただし、これを解決する方法は他にもいくつかあります。

  1. また、SWIG だけでなく実際の Java 配列を使用するコードを記述しarray_class、JNI 関数を呼び出して基になるポインターを取得することでこのインターフェイスを実装することもできます。ただし、上記と同様に、プリミティブ型ごとにこれのバージョンを作成する必要があります。

    インターフェイス ファイルは次のようになります。

    %module test
    
    %{
    void foo(void *arr);
    %}
    
    %typemap(in,numinputs=0) JNIEnv *env "$1 = jenv;"
    
    %rename(foo) fooFloat;
    %rename(foo) fooInt;
    %inline %{
    void fooFloat(JNIEnv *env, jfloatArray arr) {
      jboolean isCopy;
      foo((*env)->GetFloatArrayElements(env, arr, &isCopy));
      // Release after call with desired semantics
    }
    
    void fooInt(JNIEnv *env, jintArray arr) {
      jboolean isCopy;
      foo((*env)->GetIntArrayElements(env, arr, &isCopy));
      // Release after call
    }
    %}
    
    void foo(void *arr);
    

    次に、foowhich takefloat[]とのオーバーロードが得int[]られますSWIGTYPE_p_void

  2. ユニオンでトリックを使用できます。

    %inline %{
      union Bodge {
        void *v;
        float *f;
        int *i;
      };
    %}
    

    SWIGTYPE_p_intこれは悪い形式と考えられていますが、 からへの変換に使用できる Java インターフェイスを生成しますSWIGTYPE_p_void

  3. 次のコンパイルされたがテストされていないコードのようなものFloatArrayから継承することは可能だと思います:SWIGTYPE_p_void

    %module test
    
    %include <carrays.i>
    
    %typemap(javabase) FloatArray "SWIGTYPE_p_void"
    %typemap(javabody) FloatArray %{
      private long swigCPtr; // Minor bodge to work around private variable in parent
      private boolean swigCMemOwn;
      public $javaclassname(long cPtr, boolean cMemoryOwn) {
        super(cPtr, cMemoryOwn);
        this.swigCPtr = SWIGTYPE_p_void.getCPtr(this);
        swigCMemOwn = cMemoryOwn;
      }
    %}
    
    %array_class(float, FloatArray);
    
    void foo(void *arr);
    

    これは Java 側のポインターを複製しますが、(現在) void ポインターまたは配列クラスのいずれかで何も変更されていないため、最初に思われるほど大きな問題ではありません。(私が思う代替タイプマップを使用して基本クラスで保護するか、代わりに関数を介しcarrays.iて取得する修正バージョンを使用することもできます)swigCPtrgetCPtr

于 2012-06-13T16:03:48.770 に答える