Il tipo di base appropriato per il wrapping di std::vector
in Java è java.util.AbstractList
. L'utilizzo di java.util.Vector
come base sarebbe dispari perché si finirebbe con due set di archiviazione, uno su std::vector
e uno su java.util.Vector
.
Il motivo SWIG non lo fa per voi se è perché you can't have AbstractList<double>
in Java, deve essere AbstractList<Double>
(Double
eredita da Object
mentre double
è un tipo primitivo).
Detto tutto ciò che ho messo insieme un piccolo esempio che avvolge std::vector<double>
e std::vector<std::vector<double> >
in Java. Non è completo, ma supporta lo stile "per ogni" iterazione in Java e set()
/get()
sugli elementi. Dovrebbe essere sufficiente per mostrare come implementare altre cose come/quando le vuoi.
Parlerò del file di interfaccia in sezioni man mano che andremo, ma in fondo sarà tutto sequenziale e completo.
Partendo num.i
che definisce il nostro modulo num
:
%module num
%{
#include <vector>
#include <stdexcept>
std::vector<double> testVec() {
return std::vector<double>(10,1.0);
}
std::vector<std::vector<double> > testMat() {
return std::vector<std::vector<double> >(10, testVec());
}
%}
%pragma(java) jniclasscode=%{
static {
try {
System.loadLibrary("num");
} catch (UnsatisfiedLinkError e) {
System.err.println("Native code library failed to load. \n" + e);
System.exit(1);
}
}
%}
Abbiamo #include
s per le generati num_wrap.cxx
e due implementazioni di funzioni per la verifica (che potrebbe essere in un file separato ho appena messo qui fuori pigrizia/minimarket).
C'è anche un trucco con lo %pragma(java) jniclasscode=
che mi piace usare nelle interfacce Java SWIG per far sì che l'oggetto/DLL condiviso venga caricato in modo trasparente per l'utente dell'interfaccia.
Il prossimo nel file di interfaccia sono le parti di std::vector
che vogliamo avvolgere. Non sto usando std_vector.i
perché abbiamo bisogno di fare alcune modifiche:
namespace std {
template<class T> class vector {
public:
typedef size_t size_type;
typedef T value_type;
typedef const value_type& const_reference;
%rename(size_impl) size;
vector();
vector(size_type n);
size_type size() const;
size_type capacity() const;
void reserve(size_type n);
%rename(isEmpty) empty;
bool empty() const;
void clear();
void push_back(const value_type& x);
%extend {
const_reference get_impl(int i) throw (std::out_of_range) {
// at will throw if needed, swig will handle
return self->at(i);
}
void set_impl(int i, const value_type& val) throw (std::out_of_range) {
// at can throw
self->at(i) = val;
}
}
};
}
Il cambiamento principale è %rename(size_impl) size;
, che racconta SWIG per esporre size()
da std::vector
come size_impl
invece.Abbiamo bisogno di farlo perché Java si aspetta che size
restituisca un int
dove la versione std::vector
restituisce un size_type
che molto probabilmente non sarà int
.
Next up nel file di interfaccia diciamo che cosa classe base e le interfacce che vogliamo implementare così come scrivere del codice Java in più per costringere le cose tra le funzioni con i tipi incompatibili:
%typemap(javabase) std::vector<double> "java.util.AbstractList<Double>"
%typemap(javainterface) std::vector<double> "java.util.RandomAccess"
%typemap(javacode) std::vector<double> %{
public Double get(int idx) {
return get_impl(idx);
}
public int size() {
return (int)size_impl();
}
public Double set(int idx, Double d) {
Double old = get_impl(idx);
set_impl(idx, d.doubleValue());
return old;
}
%}
%typemap(javabase) std::vector<std::vector<double> > "java.util.AbstractList<Vector>"
%typemap(javainterface) std::vector<std::vector<double> > "java.util.RandomAccess"
%typemap(javacode) std::vector<std::vector<double> > %{
public Vector get(int idx) {
return get_impl(idx);
}
public int size() {
return (int)size_impl();
}
public Vector set(int idx, Vector v) {
Vector old = get_impl(idx);
set_impl(idx, v);
return old;
}
%}
Questo imposta una base classe di java.util.AbstractList<Double>
per std::vector<double>
e java.util.AbstractList<Vector>
per std::vector<std::vector<double> >
(Vector
è ciò che chiameremo std::vector<double>
sul lato Java dell'interfaccia).
Forniamo anche un'implementazione di get
e set
sul lato Java che può gestire il double
a Double
conversione e viceversa.
Infine nell'interfaccia aggiungiamo:
namespace std {
%template(Vector) std::vector<double>;
%template(Matrix) std::vector<vector<double> >;
}
std::vector<double> testVec();
std::vector<std::vector<double> > testMat();
Questo dice SWIG per riferirsi a std::vector<double>
(con il tipo specifico) come Vector
e analogamente per std::vector<vector<double> >
come Matrix
. Diciamo anche a SWIG di esporre le nostre due funzioni di test.
Next up, test.java
, un semplice main
in Java per esercitare il nostro codice un po ':
import java.util.AbstractList;
public class test {
public static void main(String[] argv) {
Vector v = num.testVec();
AbstractList<Double> l = v;
for (Double d: l) {
System.out.println(d);
}
Matrix m = num.testMat();
m.get(5).set(5, new Double(5.0));
for (Vector col: m) {
for (Double d: col) {
System.out.print(d + " ");
}
System.out.println();
}
}
}
per compilare ed eseguire questo facciamo:
swig -java -c++ num.i
g++ -Wall -Wextra num_wrap.cxx -shared -I/usr/lib/jvm/java-6-sun/include -I/usr/lib/jvm/java-6-sun/include/linux/ -o libnum.so
javac test.java && LD_LIBRARY_PATH=. java test
Ho provato questo con g ++ versione 4.4 e SWIG 1.3.40 su Linux/x86.
La versione completa di num.i
può essere trovata here, ma può sempre essere ricostruita da questa risposta incollando ciascuna parte in un unico file.
Le cose che non hai implementato da AbstractList
:
add()
- può essere implementato tramite push_back()
, std_vector.i anche cerca di implementare qualcosa di compatibile per impostazione predefinita, ma non funziona con il Double
vs double
problema o corrispondere il tipo di ritorno specificato nel AbstractList
(non dimenticare di incrementare modCount
)
remove()
- non eccezionale per std::vector
in termini di complessità temporale, ma non impossibile da implementare sia (lo stesso con 012.)
- Un costruttore che prende un altro
Collection
è consigliato, ma non implementato qui. Possono essere implementati nello stesso posto set()
e get()
, ma sarà necessario $javaclassname
per denominare correttamente il costruttore generato.
- Si potrebbe voler utilizzare qualcosa come this per verificare che la conversione
size_type
->int
in size()
sia sensata.
Non sei un utente SWIG, ma guardando 'std_vector.i' (le versioni di esso ho trovato on-line, in ogni caso),' size() 'si suppone essere un int'' non firmato, e SWIG si suppone di tradurre che a Java 'lungo'. Se ottieni dimensioni negative, sono pura assurdità, o sembrano come se stessero maltrattando "non firmati" come firmati? –