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