Подходящим базовым типом для переноса std::vector
в Java является java.util.AbstractList
.Использование java.util.Vector
в качестве базы было бы странным, потому что в итоге вы получили бы два набора хранилищ, один в std::vector
и один в java.util.Vector
.
. Причина, по которой SWIG не подходитэто для вас, хотя, потому что вы не можете иметь AbstractList<double>
в Java , это должно быть AbstractList<Double>
(Double
наследуется от Object
, тогда как double
является примитивным типом).
Сказав все это, я собрал небольшой пример, который прекрасно оборачивает std::vector<double>
и std::vector<std::vector<double> >
в Java.Он не завершен, но поддерживает стиль итераций «для каждого» в Java и set()
/ get()
для элементов.Этого должно быть достаточно, чтобы показать, как реализовывать другие вещи так, как / когда вы этого хотите.
Я буду обсуждать файл интерфейса в отдельных разделах, но в основном все будет последовательным и полным.
Начиная с num.i
, который определяет наш модуль 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);
}
}
%}
У нас есть #include
s для сгенерированного num_wrap.cxx
и две реализации функций для тестирования (они могут бытьв отдельном файле я просто поместил их здесь из-за лени / удобства).
Там также есть хитрость с %pragma(java) jniclasscode=
, которую я люблю использовать в Java SWIG-интерфейсах, чтобы общий объект / DLL былзагружается прозрачно для пользователя интерфейса.
Далее в файле интерфейса находятся части std::vector
, которые мы хотим обернуть.Я не использую std_vector.i
, потому что нам нужно внести несколько изменений:
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;
}
}
};
}
Основное изменение здесь %rename(size_impl) size;
, которое говорит SWIG выставлять size()
из std::vector
как size_impl
вместо.Мы должны сделать это, потому что Java ожидает, что size
вернет int
, тогда как версия std::vector
вернет size_type
, что, скорее всего, не будет int
.
Далее вфайл интерфейса, который мы сообщаем ему, какой базовый класс и интерфейсы мы хотим реализовать, а также написание дополнительного Java-кода для приведения вещей между функциями несовместимых типов:
%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;
}
%}
Это устанавливает базовый класс java.util.AbstractList<Double>
для std::vector<double>
и java.util.AbstractList<Vector>
для std::vector<std::vector<double> >
(Vector
- это то, что мы будем называть std::vector<double>
на стороне интерфейса Java).
Мы также предоставляем реализацию get
иset
на стороне Java, которая может обрабатывать преобразование double
в Double
и обратно.
Наконец, в интерфейсе мы добавляем:
namespace std {
%template(Vector) std::vector<double>;
%template(Matrix) std::vector<vector<double> >;
}
std::vector<double> testVec();
std::vector<std::vector<double> > testMat();
Это говорит SWIG о необходимостидо std::vector<double>
(с указанным типом) как Vector
и аналогично для std::vector<vector<double> >
как Matrix
.Мы также просим SWIG предоставить две наши тестовые функции.
Далее test.java
, простой main
в Java для небольшого упражнения нашего кода:
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();
}
}
}
Для сборки изапустите это, мы делаем:
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
Я проверил это на g ++ версии 4.4 и SWIG 1.3.40 на Linux / x86.
Полная версия num.i
может быть найдена здесь , но всегда можно восстановить из этого ответа, вставив каждую часть вместе в один файл.
Вещи, которые я не реализовал из AbstractList
:
add()
- может быть реализовано через push_back()
, std_vector.i даже пытается реализовать что-то совместимое по умолчанию, но это не работает с проблемой Double
vs double
или не соответствует типу возврата, указанному в AbstractList
(Не забывайте увеличивать modCount
) remove()
- не очень хорошо для std::vector
с точки зрения сложности времени, но не невозможно реализовать также (аналогично с modCount
) - Рекомендуется конструктор, который использует другой
Collection
, но здесь он не реализован.Может быть реализовано в одном месте set()
и get()
, но потребуется $javaclassname
для правильного именования сгенерированного конструктора. - Возможно, вы захотите использовать что-то вроде this дляубедитесь, что преобразование
size_type
-> int
в size()
является нормальным.