适用于将
std::vector
包装在Java中的合适基础类型是
java.util.AbstractList
。使用
java.util.Vector
作为基础类型会很奇怪,因为你最终会得到两组存储器,一组在
std::vector
中,另一组在
java.util.Vector
中。
然而,SWIG不为你自动完成此操作的原因是在Java中无法使用AbstractList<double>
,必须使用AbstractList<Double>
(Double
从Object
继承,而double
是原始类型)。
话虽如此,我已经准备了一个小例子,在Java中很好地包装了std::vector<double>
和std::vector<std::vector<double> >
。它不是完整的,但支持Java中的“for each”样式迭代以及元素的set()
/get()
。它应该足以展示如何在需要时实现其他内容。
我会按顺序详细讲解接口文件的各个部分,但基本上它将是连续完整的。
从定义模块num
的num.i
开始:
%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
以及两个函数的实现来进行测试(它们可以在单独的文件中,但出于懒惰/方便,我将它们放在这里)。
此外,在Java SWIG接口中使用的
%pragma(java) jniclasscode=
技巧可以使共享对象/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) {
return self->at(i);
}
void set_impl(int i, const value_type& val) throw (std::out_of_range) {
self->at(i) = val;
}
}
};
}
这里的主要改变是
%rename(size_impl) size;
,它告诉 SWIG 将
std::vector
的
size()
暴露为
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;
}
%}
这将为
std::vector<double>
设置一个基类为
java.util.AbstractList<Double>
,并且为
std::vector<std::vector<double>>
设置一个基类为
java.util.AbstractList<Vector>
(在接口的Java端我们将称之为
Vector
,即
std::vector<double>
)。
此外,我们还提供了Java端的
get
和
set
实现,可以处理
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公开我们的两个测试函数。
接下来是一个简单的Java
main
,用于对我们的代码进行一些操作:
test.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
我在Linux/x86上使用g++ 4.4版本和SWIG 1.3.40进行了测试。
num.i
的完整版本可以在这里找到,但是可以通过将每个部分粘贴到一个文件中来重构此答案以获取完整版本。
我从AbstractList
中未实现的内容:
add()
- 可以通过push_back()
实现,默认情况下std_vector.i甚至尝试实现与之兼容的东西,但它不能解决Double
与double
问题或匹配AbstractList
中指定的返回类型(不要忘记增加modCount
)
remove()
- 对于std::vector
来说时间复杂度不太好,但也不是不可能实现(同样适用于modCount
)
- 建议使用另一个
Collection
作为构造函数,但这里没有实现。可以在与set()
和get()
相同的位置实现,但需要$javaclassname
来正确命名生成的构造函数。
- 您可能希望使用类似于这样的方法来检查
size_type
->int
转换是否合理。
std_vector.i
(无论我在网上找到的哪个版本),size()
应该是一个unsigned int
,而SWIG应该将其转换为Java的long
。如果你得到了负数大小,它们是纯粹的无意义,还是看起来像是将unsigned
误解为有符号的? - David Moles