Sono un po 'perso con typemaps in swig e come utilizzare gli array. Ho preparato un esempio funzionante che usa array tra java e c usando swig, ma non so se è il modo corretto per farlo.Modo corretto per interagire con gli array utilizzando SWIG
Fondamentalmente voglio passare un array di byte byte[]
da java a c come un carattere di segno * `+ la sua dimensione, modificarlo in c e vedere le modifiche in java e creare un array in c e usarlo in Java.
devo dare un'occhiata a domande theese: How to pass array(array of long in java) from Java to C++ using Swig, Pass an array to a wrapped function as pointer+size or range, How can I make Swig correctly wrap a char* buffer that is modified in C as a Java Something-or-other?
E infatti utilizzato le soluzioni come una guida per fare l'esempio.
Questo è il mio codice nel file 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;
}
Questo è il file .i per sorso (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 infine il codice Java per testare iT:
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();
}
}
l'esempio funziona, ma non sono sicuro che questo è il modo corretto, voglio dire:
c'è un modo più semplice per ottenere lo stesso risultato?
questo codice ha perdite di memoria (da una parte penso che ci sia perché io faccio un calloc ma non lo libero, ma d'altra parte lo passo alla SetByteArrayRegion, quindi magari liberarlo causerebbe un errore)?
la SetByteArrayRegion copia i valori o solo il riferimento ?, ad esempio se invece di eseguire effettivamente un calloc, cosa succede se si ottiene un array da un oggetto C++ per riferimento che verrà distrutto quando esce dall'ambito?
L'array restituito a Java viene liberato correttamente quando lo si annulla?
c'è un modo per specificare da dove si applica una typemap ?, voglio dire, nel codice .i ho fornito una typemap per ogni funzione, dove penso che potrei riutilizzarne alcuni, ma se ci fossero altri funzionano con gli stessi parametri che non voglio tipizzarli, come posso farlo, potrei non essere in grado di modificare il nome dei parametri delle funzioni.
Ho visto la possibilità dei carriys.i descritta in questa domanda How do I pass arrays from Java to C++ using Swig?, ma questo implica che se la dimensione dell'array è di 1000 elementi e voglio inviarlo tramite un socket Java o creare una stringa da esso, ho effettuare 1 chiamata JNI per ogni elemento dell'array. E io in realtà voglio un byte[]
nel lato Java, non un insieme di funzioni per accedere all'array di underlaying, quindi il codice già esistente funziona senza modifiche.
Contesto: Il motivo che voglio per raggiungere questo obiettivo è che v'è una libreria che hanno alcune funzionalità, ma la parte importante è che permette di importare ed esportare dati dalla libreria facendo uso di Google Buffer di protocolli.Così il codice relativo a questa domanda si presenta così:
class SomeLibrary {
bool export(const std::string & sName, std::string & toExport);
bool import(const std::string & sName, const std::string & toImport);
}
Il fatto è che Protobuf in C++ utilizza std :: string per memorizzare i dati, ma questi dati sono binari in modo che non possa essere restituito come Java normale Stringa perché viene troncata, più di questo in Swig: convert return type std::string(binary) to java byte[].
Quindi la mia idea è quella di tornare a Java un byte[]
per il serializzato Protobuf (come fa la versione Java di buffer Protocol) e accettare byte[]
per analizzare protobufs. Per evitare di ottenere SWIGTYPE_p_std_string
nel secondo argomento delle esportazioni, e avendo String per il secondo argomento di importazione y hanno avvolto le due funzioni utilizzando% estendere, in questo modo:
%extend SomeLibrary{
bool export(const std::string & sName, char ** toExportData, int * toExportLength);
bool import(const std::string & sName, char * toImportData, int toImportLength);
}
E ora dovrei essere in grado di effettuare i typemaps .
Ma per essere più generale, ho chiesto il generale di manipolare gli array da Java a sorso avendo il nativo di Java byte[]
.
Credo che stai facendo è più complicato di quanto dovrebbe essere :). Ho ragione nel pensare di voler costruire un grande array in Java e poi passarlo in una funzione C++? Posso mostrare una soluzione semplice a questo e indirizzare alcune delle altre domande lungo la strada troppo speranzosamente. Ad esempio stai provando a passare un 'byte []' in una funzione come 'void foo (const char * arr, size_t len);'? – Flexo
@Flexo - Penso anche che lo sto facendo troppo complicato. Ho anche modificato la domanda fornendo un po 'più di contesto. –
Per rispondere a un paio di altri punti: 'SetByteArrayRegion' copia i dati. L'array Java sarà gestito dai soliti meccanismi Java. Viene creato localmente alla chiamata JNI e verrà rilasciato un punto dopo la rimozione dell'ultimo riferimento ad esso. – Flexo