Maneira correta de interagir com arrays usando o SWIG

Estou um pouco perdido com typemaps no swig e como usar arrays. Eu preparei um exemplo de trabalho que usa arrays entre java e c usando swig, mas não sei se é a maneira correta de fazê-lo.

Basicamente eu quero passar uma matriz de bytesbyte[] de java para c como um caractere ´signed * `+ é size, modifique-o em c e veja as mudanças em java e crie um array em c use-o em Java.

Eu dei uma olhada nestas questões:Como passar array (array de long em java) de Java para C ++ usando Swig, Passar uma matriz para uma função empacotada como ponteiro + tamanho ou intervalo, Como posso fazer com que o Swig envolva corretamente um buffer char * que é modificado em C como algo Java ou algo assim?

E de fato usou as soluções como um guia para fazer o exemplo.

Este é o meu código no arquivo 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;
}

Este é o arquivo .i para swig (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"

E finalmente o código Java para testá-lo:

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();
    }
}

O exemplo funciona, mas não tenho certeza se essa é a maneira correta, quero dizer:

Existe uma maneira mais fácil de alcançar o mesmo resultado?

Esse código tem vazamentos de memória (por um lado eu acho que existe porque eu faço um calloc mas eu não o libero, mas por outro lado eu passo para o SetByteArrayRegion, então talvez liberá-lo causaria um erro)?

o SetByteArrayRegion copia os valores ou apenas a referência ?, por exemplo, se em vez de realmente fazer um calloc, o que se obter uma matriz de um objeto c + + por referência que vai ser destruir quando sai do escopo?

é a matriz retornada para Java corretamente liberada ao anulá-lo?

Existe uma maneira de especificar onde para onde um tipo de mapa se aplica ?, Quer dizer, no código .i eu forneci um mapa de tipo para cada função, onde eu acho que eu poderia reutilizar alguns deles, mas se houvesse outras funções com o mesmo parâmetros que eu não quero mapeá-los, como posso fazer isso, talvez não consiga modificar o nome dos parâmetros das funções.

Eu vi a possibilidade de carrays.i descrita nesta questãoComo faço para passar matrizes de Java para C ++ usando Swig?, mas isso implica que, se o tamanho da matriz é de 1000 itens e eu quero enviá-lo através de um Java Socket ou criar uma String a partir dele, eu tenho que fazer 1 JNI Call para cada item da matriz. E eu realmente quero umbyte[] no lado do Java, não é um conjunto de funções para acessar o array de underlaying, então o código já existente funciona sem modificações.

Contexto: A razão pela qual eu quero alcançar isso é que existe uma biblioteca que tem alguma funcionalidade, mas a parte importante aqui é que ela permite importar e exportar dados da biblioteca usando os Buffers de Protocolos do Google. Então o código relacionado a essa pergunta é assim:

class SomeLibrary {

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

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

}

A questão é que o Protobuf em C ++ usa std :: string para armazenar os dados, mas esses dados são binários, portanto, não podem ser retornados como uma string Java normal, porque ela fica truncada, mais disso emSwig: converte o tipo de retorno std :: string (binário) para java byte [].

Então minha ideia é retornar ao Javabyte[] para o Protobuf serializado (assim como a versão Java dos buffers de protocolo) e aceitarbyte[] para analisar protobufs. Para evitar ficarSWIGTYPE_p_std_string no segundo argumento da exportação, e ter String para o segundo argumento de importação y envolveu ambas as funções usando% extend, assim:

%extend SomeLibrary{

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

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

}

E agora eu deveria poder fazer os typemaps.

Mas, para ser mais geral, eu pedi o general de manipular matrizes de Java para SWIG,tendo o Java nativobyte[].

questionAnswers(2)

yourAnswerToTheQuestion