Ответ 1
Соответствующим базовым типом для упаковки 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
для сгенерированных 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
to 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
vsDouble
или соответствует типу возврата, указанному вAbstractList
(Не забудьте прираститьmodCount
) -
remove()
- не очень подходит дляstd::vector
с точки зрения сложности времени, но не невозможно реализовать (аналогичноmodCount
) - Рекомендуется использовать конструктор, который принимает другой
Collection
, но не реализован. Может быть реализовано в том же местеset()
иget()
, но ему потребуется$javaclassname
, чтобы правильноменовать сгенерированный конструктор. - Возможно, вы захотите использовать что-то вроде этого, чтобы проверить, что преобразование
size_type
→int
вsize()
является нормальным.