Prawidłowy sposób interakcji z tablicami za pomocą SWIG

Jestem trochę zagubiony z typemapami w swig i jak używać tablic. Przygotowałem działający przykład, który używa tablic między java i c, używając swig, ale nie wiem, czy jest to właściwy sposób.

Zasadniczo chcę przekazać tablicę bajtówbyte[] od java do c jako ignedsigned char * `+ jego rozmiar, zmodyfikuj go w c i zobacz zmiany w java i utwórz tablicę w c i użyj jej w Javie.

Spojrzałem na te pytania:Jak przekazać tablicę (tablicę długich w java) z Java do C ++ za pomocą Swig, Przekazuj tablicę do funkcji opakowanej jako wskaźnik + rozmiar lub zakres, Jak sprawić, by Swig poprawnie owinął bufor char * zmodyfikowany w C jako Java Something-or-other?

I faktycznie użyłem tych rozwiązań jako przewodnika, aby dać przykład.

To jest mój kod w pliku 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;
}

To jest plik .i dla 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"

I wreszcie kod Java do przetestowania:

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

Przykład działa, ale nie jestem pewien, czy jest to właściwy sposób, to znaczy:

czy istnieje łatwiejszy sposób osiągnięcia tego samego wyniku?

czy ten kod ma wycieki pamięci (z jednej strony myślę, że istnieje, ponieważ wykonuję wywołanie, ale go nie zwalniam, ale z drugiej strony przekazuję go do SetByteArrayRegion, więc może uwolnienie go spowodowałoby błąd)?

czy SetByteArrayRegion kopiuje wartości lub tylko odwołanie?, na przykład jeśli zamiast wykonać wywołanie, co jeśli uzyskamy tablicę z obiektu c ++ przez odniesienie, które zostanie zniszczone po wyjściu z zakresu?

czy tablica zwrócona do Java została poprawnie zwolniona po jej unieważnieniu?

czy istnieje sposób na określenie miejsca, do którego ma zastosowanie typografia ?, mam na myśli, że w kodzie .i udostępniłem typemap dla każdej funkcji, gdzie, jak sądzę, mogę ponownie wykorzystać niektóre z nich, ale jeśli istnieją inne funkcje z tym samym parametry, których nie chcę wpisywać z klawiatury, jak mogę to zrobić, może nie być w stanie zmodyfikować nazwy parametrów funkcji.

Widziałem możliwość carrays.i opisaną w tym pytaniuJak przekazać tablice z Java do C ++ za pomocą Swig?, ale oznacza to, że jeśli rozmiar tablicy wynosi 1000 elementów i chcę wysłać ją przez gniazdo Java lub utworzyć z niej ciąg znaków, muszę wykonać 1 wywołanie JNI dla każdego elementu tablicy. I naprawdę chcębyte[] po stronie Java, nie zestaw funkcji dostępu do tablicy podkładania, więc już istniejący kod działa bez modyfikacji.

Kontekst: Powodem, dla którego chcę to osiągnąć, jest biblioteka, która ma pewne funkcje, ale ważną częścią jest to, że pozwala ona na importowanie i eksportowanie danych z biblioteki przy użyciu buforów protokołów Google. Kod związany z tym pytaniem wygląda więc tak:

class SomeLibrary {

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

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

}

Rzecz w tym, że Protobuf w C ++ używa std :: string do przechowywania danych, ale dane te są binarne, więc nie mogą być zwrócone jako normalny ciąg Java, ponieważ jest on obcięty, więcej wSwig: konwertuj typ zwracany std :: string (binarny) na bajt java [].

Więc moim pomysłem jest powrót do Java abyte[] dla serializowanego Protobuf (podobnie jak wersja buforów Java w protokole) i zaakceptujbyte[] do analizowania protobufów. Aby uniknąćSWIGTYPE_p_std_string w drugim argumencie eksportu i posiadanie ciągu dla drugiego argumentu importu owija obie funkcje za pomocą% extend, tak:

%extend SomeLibrary{

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

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

}

A teraz powinienem móc tworzyć typemapy.

Ale żeby być bardziej ogólnym, poprosiłem o ogólne manipulowanie tablicami z Java na SWIG,mający natywna Javabyte[].

questionAnswers(2)

yourAnswerToTheQuestion