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[]
。