6

swigのタイプマップと配列の使い方に少し迷っています。swigを使用してjavaとcの間の配列を使用する実用的な例を用意しましたが、それが正しい方法かどうかはわかりません。

基本的に、バイト配列byte[]をjavaからcに「signedchar *」+そのサイズとして渡し、cで変更して、javaでの変更を確認し、cで配列を作成して、Javaで使用します。

私はこれらの質問を見てきました:Swig を 使用してJavaからC ++に配列(Javaではlongの配列)を渡す方法、ポインタ+サイズまたは範囲としてラップされた関数に配列を 渡す方法、Swigにcharを正しくラップさせる方法* CでJavaの何かとして変更されたバッファ?

そして実際には、例を作成するためのガイドとしてソリューションを使用しました。

これはファイル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(arrays.i)の.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に返される配列は正しく解放されますか?

タイプマップをどこからどこに適用するかを指定する方法はありますか?つまり、.iコードで、各関数のタイプマップを提供しました。ここで、それらの一部を再利用できると思いますが、同じ関数が他にある場合はタイプマップしたくないパラメーター、どうすればそれができますか、関数のパラメーター名を変更できない場合があります。

この質問で説明されているcarrays.iの可能性を見てきました。Swigを使用してJavaからC++に配列を渡すにはどうすればよいですか?、ただし、配列のサイズが1000アイテムで、Javaソケットを介して送信するか、そこから文字列を作成する場合は、配列アイテムごとに1つのJNI呼び出しを行う必要があります。そして、私は実際にはbyte[]、下にある配列にアクセスするための関数のセットではなく、Java側にを置きたいので、既存のコードは変更なしで機能します。


コンテキスト:これを実現したい理由は、いくつかの機能を備えたライブラリがあるためですが、ここで重要なのは、Googleプロトコルバッファを使用してライブラリからデータをインポートおよびエクスポートできることです。したがって、この質問に関連するコードは次のようになります。

class SomeLibrary {

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

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

}

C++のProtobufはstd::stringを使用してデータを格納しますが、このデータはバイナリであるため、切り捨てられるため通常のJava文字列として返すことはできません。Swig:convert return type std :: string(binary)からjavabyte[]へ

したがって、私の考えはbyte[]、シリアル化されたProtobufのJava aに戻り(JavaバージョンのProtocolバッファーと同様に)、byte[]protobufの解析を受け入れることです。SWIGTYPE_p_std_stringエクスポートの2番目の引数に入るのを避け、インポートyの2番目の引数にStringを使用すると、次のように%extendを使用して両方の関数がラップされます。

%extend SomeLibrary{

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

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

}

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

しかし、より一般的にするために、ネイティブJava使用して、JavaからSWIGへの配列の操作の一般的な方法を求めましたbyte[]

4

2 に答える 2

7

carrays.iを自動的に割引しないでください。つまり、SWIGにはすでにいくつかの便利なタイプマップがあります。

%module test

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

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

これは、Javaインターフェースで関数を生成します。

public static void some_func(byte[] str)

つまり、通常のようにJavaで構築できる配列を取り、ポインタと長さを入力します。ほとんど無料です。

現状のコードはほぼ確実にリークしますfree()。新しいJava配列にコピーされたら、argoutタイプマップ内で呼び出して、割り当てたメモリを解放する必要があります。

パラメータのタイプと名前の両方でタイプマップを選択的に適用できます。タイプマップマッチングルールの詳細については、このドキュメントを参照してください。上記の例のように、他の方法では使用されないタイプマップを明示的に使用するように要求することもできます%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);

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

于 2012-09-19T18:58:50.527 に答える
0

ここで十分に文書化されています:http ://swig.org/Doc3.0/SWIGDocumentation.html#Java_binary_char

以下のインターフェースファイルは、Javaでbyte[]メソッドを生成します。

%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");
}
%}
于 2018-03-10T12:48:56.490 に答える