El tipo de base apropiado para envolver std::vector
en Java es java.util.AbstractList
. Usar java.util.Vector
como base sería extraño porque terminaría con dos juegos de almacenamiento, uno en el std::vector
, y el otro en el java.util.Vector
.
La razón TRAGO no hace esto para usted, aunque se debe a you can't have AbstractList<double>
in Java, que tiene que ser AbstractList<Double>
(Double
hereda de Object
mientras que double
es un tipo primitivo).
Habiendo dicho todo eso, he reunido un pequeño ejemplo que envuelve std::vector<double>
y std::vector<std::vector<double> >
muy bien en Java. No está completo, pero admite el estilo de iteración "para cada uno" en Java y set()
/get()
en los elementos. Debería ser suficiente para mostrar cómo implementar otras cosas cuando/cuando las desee.
Hablaré a través del archivo de interfaz en las secciones a medida que avanzamos, pero básicamente todo será secuencial y completo.
A partir de num.i
que define nuestro módulo 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);
}
}
%}
Tenemos #include
s para los generados num_wrap.cxx
y dos implementaciones de funciones para la prueba (que podría ser en un archivo separado acabo de poner aquí de pereza/conveniencia).
También hay un truco allí con el %pragma(java) jniclasscode=
que me gusta usar en las interfaces Java SWIG para hacer que el objeto compartido/DLL se cargue de forma transparente para el usuario de la interfaz.
El siguiente en el archivo de interfaz son las partes de std::vector
que queremos envolver. No estoy usando std_vector.i
porque tenemos que hacer algunos cambios:
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;
}
}
};
}
El cambio principal es %rename(size_impl) size;
, que dice TRAGO para exponer size()
de std::vector
como size_impl
lugar.Necesitamos hacer esto porque Java espera que size
devuelva un int
donde la versión std::vector
devuelve un size_type
que muy probablemente no será int
.
siguiente en el archivo de interfaz que indicarle lo que la clase base y las interfaces que queremos implementar, así como escribir algo de código Java adicional para obligar a las cosas entre funciones con tipos incompatibles:
%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;
}
%}
Esto establece una base clase de java.util.AbstractList<Double>
para std::vector<double>
y java.util.AbstractList<Vector>
para std::vector<std::vector<double> >
(Vector
es lo que llamaremos std::vector<double>
en el lado de Java de la interfaz).
También suministramos una implementación de get
y set
en el lado de Java que puede manejar la conversión double
-Double
y viceversa.
Por último, en la interfaz que añadir:
namespace std {
%template(Vector) std::vector<double>;
%template(Matrix) std::vector<vector<double> >;
}
std::vector<double> testVec();
std::vector<std::vector<double> > testMat();
Esto le dice TRAGO para referirse a std::vector<double>
(con el tipo específico) como Vector
y lo mismo para std::vector<vector<double> >
como Matrix
. También le decimos a SWIG que exponga nuestras dos funciones de prueba.
El siguiente, test.java
, un simple main
en Java para ejercer nuestro código un poco:
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();
}
}
}
para generar y ejecutar esto lo hacemos:
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
He probado esto con g ++ versión 4.4 y SWIG 1.3.40 en Linux/x86.
La versión completa de num.i
se puede encontrar en here, pero siempre se puede reconstruir a partir de esta respuesta pegando cada parte en un solo archivo.
Cosas que no he implementado desde AbstractList
:
add()
- se puede implementar a través de push_back()
, incluso std_vector.i intenta poner en práctica algo compatible de forma predeterminada, pero no funciona con la Double
vs double
problema o que coincida con el tipo de retorno se especifica en AbstractList
(no se olvide de incrementar modCount
)
remove()
- no es genial para std::vector
en cuanto a la complejidad del tiempo, pero no imposible de poner en práctica ni (lo mismo con 012.)
- Se recomienda un constructor que tome otro
Collection
, pero no implementado aquí. Puede implementarse en el mismo lugar set()
y get()
, pero necesitará $javaclassname
para nombrar correctamente el constructor generado.
- Es posible que desee utilizar algo como this para comprobar que la conversión
size_type
->int
en size()
es correcta.
No soy un usuario de SWIG, pero al mirar 'std_vector.i' (las versiones que encuentro en línea, de todos modos),' size() 'se supone que es' unsigned int', y SWIG se supone que traduce eso a un Java 'largo'. Si obtiene tamaños negativos, ¿son tonterías sin sentido, o se ven como si estuvieran maltratando 'unsigned' como firmado? –