2012-09-19 10 views
5

swigのtypemapsと配列の使い方が少し失われています。私はswigを使ってjavaとcの間の配列を使用する実装例を用意しましたが、それが正しい方法かどうかはわかりません。SWIGを使って配列とやりとりする正しい方法

基本的に私は、バイト配列byte[]をjavaからcのaigned char * `+サイズに渡したいと思っています。それをcで修正してjavaの変更を見て、cで配列を作成してJavaで使用します。 How can I make Swig correctly wrap a char* buffer that is modified in C as a Java Something-or-other?

、実際に は、例を作るためのガイドとしてのソリューションを使用し、 How to pass array(array of long in java) from Java to C++ using SwigPass an array to a wrapped function as pointer+size or range

私はtheeseの質問を見てみる必要があります。

これは、ファイルarrays.hで私のコードです:

#include <iostream> 

bool createArray(signed char ** arrCA, int * lCA){ 
    *lCA = 10; 
    *arrCA = (signed char*) calloc(*lCA, sizeof(signed char)); 

    for(int i = 0; i < *lCA; i++){ 
     (*arrCA)[i] = i; 
    } 

    return *arrCA != NULL; 
} 

bool readArray(const signed char arrRA[], const int lRA){ 
    for(int i = 0; i < lRA; i++){ 
     std::cout << ((unsigned int) arrRA[i]) << " "; 
    } 
    std::cout << std::endl; 
    return true; 
} 

bool modifyArrayValues(signed char arrMA[], const int lMA){ 
    for(int i = 0; i < lMA; i++){ 
     arrMA[i] = arrMA[i] * 2; 
    } 
    return true; 
} 


bool modifyArrayLength(signed char arrMALIn[], int lMALIn, signed char ** arrMALOut, int * lMALOut){ 

    *lMALOut = 5; 
    *arrMALOut = (signed char*) calloc(*lMALOut, sizeof(signed char)); 

    for(int i = 0; i < *lMALOut; i++){ 
     (*arrMALOut)[i] = arrMALIn[i]; 
    } 
    return true; 
} 

これはSWIGのため.iファイル(arrays.i)である:テストへ

%module arrays 

%{ 
    #include "arrays.h" 
%} 

%typemap(jtype) bool createArray "byte[]" 
%typemap(jstype) bool createArray "byte[]" 
%typemap(jni) bool createArray "jbyteArray" 
%typemap(javaout) bool createArray { return $jnicall; } 
%typemap(in, numinputs=0) signed char ** arrCA (signed char * temp) "$1=&temp;" 
%typemap(in, numinputs=0) int * lCA (int l) "$1=&l;" 
%typemap(argout) (signed char ** arrCA, int * lCA) { 
    $result = JCALL1(NewByteArray, jenv, *$2); 
    JCALL4(SetByteArrayRegion, jenv, $result, 0, *$2, (const jbyte*) *$1); 
} 
%typemap(out) bool createArray { 
    if (!$1) { 
     return NULL; 
    } 
} 


%typemap(jtype) (const signed char arrRA[], const int lRA) "byte[]" 
%typemap(jstype) (const signed char arrRA[], const int lRA) "byte[]" 
%typemap(jni) (const signed char arrRA[], const int lRA) "jbyteArray" 
%typemap(javain) (const signed char arrRA[], const int lRA) "$javainput" 

%typemap(in,numinputs=1) (const signed char arrRA[], const int lRA) { 
    $1 = JCALL2(GetByteArrayElements, jenv, $input, NULL); 
    $2 = JCALL1(GetArrayLength, jenv, $input); 
} 

%typemap(freearg) (const signed char arrRA[], const int lRA) { 
    // Or use 0 instead of ABORT to keep changes if it was a copy 
    JCALL3(ReleaseByteArrayElements, jenv, $input, $1, JNI_ABORT); 
} 


%typemap(jtype) (signed char arrMA[], const int lMA) "byte[]" 
%typemap(jstype) (signed char arrMA[], const int lMA) "byte[]" 
%typemap(jni) (signed char arrMA[], const int lMA) "jbyteArray" 
%typemap(javain) (signed char arrMA[], const int lMA) "$javainput" 

%typemap(in, numinputs=1) (signed char arrMA[], const int lMA) { 
    $1 = JCALL2(GetByteArrayElements, jenv, $input, NULL); 
    $2 = JCALL1(GetArrayLength, jenv, $input); 
} 

%typemap(freearg) (signed char arrMA[], const int lMA) { 
    JCALL3(ReleaseByteArrayElements, jenv, $input, $1, 0); 
} 

%typemap(jtype) (signed char arrMALIn[], int lMALIn) "byte[]" 
%typemap(jstype) (signed char arrMALIn[], int lMALIn) "byte[]" 
%typemap(jni) (signed char arrMALIn[], int lMALIn) "jbyteArray" 
%typemap(javain) (signed char arrMALIn[], int lMALIn) "$javainput" 

%typemap(in, numinputs=1) (signed char arrMALIn[], int lMALIn) { 
    $1 = JCALL2(GetByteArrayElements, jenv, $input, NULL); 
    $2 = JCALL1(GetArrayLength, jenv, $input); 
} 

%typemap(freearg) (signed char arrMALIn[], int lMALIn) { 
    JCALL3(ReleaseByteArrayElements, jenv, $input, $1, JNI_ABORT); 
} 

%typemap(jtype) bool modifyArrayLength "byte[]" 
%typemap(jstype) bool modifyArrayLength "byte[]" 
%typemap(jni) bool modifyArrayLength "jbyteArray" 
%typemap(javaout) bool modifyArrayLength { return $jnicall; } 
%typemap(in, numinputs=0) signed char ** arrMALOut (signed char * temp) "$1=&temp;" 
%typemap(in, numinputs=0) int * lMALOut (int l) "$1=&l;" 
%typemap(argout) (signed char ** arrMALOut, int * lMALOut) { 
    $result = JCALL1(NewByteArray, jenv, *$2); 
    JCALL4(SetByteArrayRegion, jenv, $result, 0, *$2, (const jbyte*) *$1); 
} 
%typemap(out) bool modifyArrayLength { 
    if (!$1) { 
     return NULL; 
    } 
} 


%include "arrays.h" 

そして最後にJavaコードそれ:

public class Run{ 

    static { 
     System.loadLibrary("Arrays"); 
    } 

    public static void main(String[] args){ 

     byte[] test = arrays.createArray(); 

     printArray(test);  

     arrays.readArray(test); 

     arrays.modifyArrayValues(test); 

     printArray(test); 

     byte[] test2 = arrays.modifyArrayLength(test); 

     printArray(test2); 

    } 

    private static void printArray(byte[] arr){ 

     System.out.println("Array ref: " + arr); 

     if(arr != null){ 
      System.out.println("Array length: " + arr.length); 

      System.out.print("Arrays items: "); 

      for(int i =0; i < arr.length; i++){ 
       System.out.print(arr[i] + " "); 
      } 
     } 
     System.out.println(); 
    } 
} 

例に動作しますが、私はそれが正しい方法であることを確認しないんだけど、私は意味:

同じ結果を達成するための簡単な方法はありますか?

このコードはメモリリークを持っていますか?(私はcallocを実行するが、私はそれを解放しないが、もう一方ではSetByteArrayRegionに渡すので、エラー)?

SetByteArrayRegionは値をコピーするか、参照のみをコピーするのですか?たとえば、実際にcallocを実行する代わりに、スコープを終了したときに破棄される参照によってC++オブジェクトから配列を取得する場合は?

は、無効にすると正しく解放されたJavaに返される配列ですか?

typemapがどこからどこに適用されるのかを指定する方法はありますか?iコードでは、各関数のtypemapを提供していますが、それらのいくつかを再利用できると思いますが、他のものは同じパラメータを使って機能しますが、それらをtypemapしたくないのですが、どうすれば関数のパラメータ名を変更できないかもしれません。

私はこの質問How do I pass arrays from Java to C++ using Swig?に記載されているcarrays.i可能性を見てきましたが、それは配列のサイズが1000項目で、Javaソケット経由で送信したい場合や、各配列項目に対して1つのJNI呼び出しを行います。そして、私は実際には、Java側ではbyte[]を、アンダーレイ配列にアクセスする関数のセットではないので、既存のコードは変更なしで動作します。


コンテキスト: 私はこれを達成したい理由は、いくつかの機能を持っているライブラリが存在することであるが、ここで重要な部分は、それがグーグルのライブラリ生かしからデータをインポートおよびエクスポートすることが可能であることですプロトコルバッファ。

class SomeLibrary { 

    bool export(const std::string & sName, std::string & toExport); 

    bool import(const std::string & sName, const std::string & toImport); 

} 

事はC++でいるProtobufは、データを格納するためのstd ::文字列を使用していることですが、それは通常のJavaのように戻すことはできませんので、このデータはバイナリです:だから、この質問に関連するコードは次のようになります文字列が切り詰められるため、Swig: convert return type std::string(binary) to java byte[]ではより多くの文字列が得られます。

私の考えは、シリアル化されたProtobuf(プロトコルバッファのJava版と同じように)のbyte[]にJavaに戻り、protobufsを解析するためにbyte[]を受け入れることです。輸出の第2引数にSWIGTYPE_p_std_stringを取得し、%を使用して両方の機能をラップしている輸入yの第二引数の文字列を避けるこのように、拡張するには:

%extend SomeLibrary{ 

    bool export(const std::string & sName, char ** toExportData, int * toExportLength); 

    bool import(const std::string & sName, char * toImportData, int toImportLength); 

} 

を、今私は、タイプマップを作ることができるはずです。

しかし、より一般的には、私はJavaからSWIGへの配列の操作の一般的な質問ネイティブJava byte[]を持っています。

+0

http://swig.org/Doc3.0/SWIGDocumentation.html#Java_binary_char

以下のインタフェースファイルは、Javaのバイト[]メソッドが生成されます。私はあなたがJavaで大きな配列を作成し、それをC++関数に渡したいと思っているのを修正していますか?私はそれに簡単な解決策を示し、あまりにもうまくいけば、他の質問のいくつかに対処することができます。例えば、 'void []'を 'void foo(const char * arr、size_t len);のような関数に渡そうとしていますか? – Flexo

+0

@Flexo - 私はそれもやっていると思うよ。私は文脈をもう少し詳しく説明しています。 –

+0

さらに多くの点に答えるには、 'SetByteArrayRegion'がデータをコピーします。 Java配列は、通常のJavaメカニズムによって管理されます。 JNIコールのローカルに作成され、最後の参照が削除された後、ある時点で解放されます。 – Flexo

答えて

5

carrays.iを自動的には割引しないでください。

Javaインタフェース内の関数を生成
%module test 

%apply(char *STRING, size_t LENGTH) { (char *str, size_t len) }; 

%inline %{ 
void some_func(char *str, size_t len) { 
} 
%} 

:それはSWIGは、すでにいくつかの便利なタイプマップを有することを特徴とする

public static void some_func(byte[] str) 

それは通常のようにあなたはJavaで構築することができる配列を受け取り、ポインタに埋めIEとあなたのための長さ。ほとんど無料です。

あなたのコードはほぼ確実にリークしています。新しいJava配列にコピーされると、割り当てられたメモリを解放するために、argoutの型マップ内にfree()と呼ぶことをお勧めします。

タイプマップの両方で、タイプマップを選択的に適用することができます。 See this document for more on typemap matching rules。上記の例のように、%applyと一緒に使用されないタイプマップを明示的に使用するように要求することもできます。 (実際にはタイプマップをコピーするので、それらのうちの1つだけを変更した場合、一般的なケースでは置き換えられません)

一般に、JavaからC++への配列の渡し方や既知のサイズの配列サイズ情報がより明白なので、C++からJavaに戻るためのものより簡単です。

私の提案は、Javaの割り当てと、2つのモード(1つは必要なサイズを示し、もう1つは実際に動作する)で動作するように配列を拡張する関数を設計することです。

ssize_t some_function(char *in, size_t in_sz) { 
    if (in_sz < the_size_I_need) { 
    return the_size_I_need; // query the size is pretty fast 
    } 

    // do some work on in if it's big enough 

    // use negative sizes or exceptions to indicate errors 

    return the_size_I_really_used; // send the real size back to Java 
} 

あなたはJavaで、次のような何かをできるようになること:あなたがそれを行うかもしれません

int sz = module.some_function(new byte[0]); 
byte result[] = new byte[sz]; 
sz = module.some_function(result); 

注意を、彼らはnullことはできませんのでnew byte[0]が必要とされているデフォルトのタイプマップを持つこと配列として使用する場合は、必要に応じてこれを可能にするタイプマップを追加するか、空の配列を必要としないオーバーロードを提供するために%extendを使用します。

+0

私はこのソリューションをテストして、入力値のためにうまくいきます。出力する前に必要なサイズを知ることができません。つまり、シリアル化を行うときにどれくらいのスペースが必要なのかを知るためにprotobufをシリアル化しています。シリアル化されたデータを破棄したいのですが、それは配列に収まらないからです。既存のコードが変更されずに動作しないため、データを含む配列よりもJavaの配列に戻っても大したことはありません。 Java側で配列をコピーすることで避けられます)。私は解決策への入力とtypemapsを元のtypemapsで出力するためにtypemapを変更しました –

+0

@JavierMrそれはまた意味があります - 私は入力(またはtypemapsが値をコピーするので、入力/出力を組み合わせる)新しい配列の作成についてはhttp://stackoverflow.com/a/12196522/168175を参照してください。 – Flexo

+0

ありがとう、私がしたswigについてのすべての質問のあなたの助けのために、たくさんありました。 –

0

これはよくここに文書化されます:私はあなたはそれが:)する必要がありますよりも、それはより複雑になっていると思います

%apply (char *STRING, size_t LENGTH) { (const char data[], size_t len) } 
%inline %{ 
void binaryChar1(const char data[], size_t len) { 
    printf("len: %d data: ", len); 
    for (size_t i=0; i<len; ++i) 
    printf("%x ", data[i]); 
    printf("\n"); 
} 
%} 
関連する問題