我有一些 native C++ 代码,我正在使用 SWIG 将它们转换为 Java,以便我的 Java 应用程序可以使用它。特别是有一些函数返回 std::vector。这是我的界面文件的片段:
%include "std_vector.i"
namespace std {
%template(Vector) vector<double>;
%template(Matrix) vector<vector<double> >;
}
%include "std_string.i"
std_string.i
和 std_vector.i
包含在我正在使用的 SWIG 构建中。我的第一个惊喜是 Java 输出包括 SWIG 的“自己”版本的 Vector 类(而不是使用 java.util.Vector
)。我真正的问题是从这些函数返回的 vector 似乎不起作用。例如,我无法使用 get()
检索它们的内容。 (有时会导致程序崩溃)或 size()
函数返回负值。我知道Vector
s 包含数据,因为我编码了相同函数的“字符串”版本,这些函数只是遍历 Vector
s(回到 native C++ 代码中)并返回以逗号分隔的内容 String
值(value)。虽然这是一个有效的解决方法,但最终我希望它能够正常工作,我能够接收和操作 Vectors
.任何帮助/提示将不胜感激。
最佳答案
用于包装的适当基础类型 std::vector
在 Java 中是 java.util.AbstractList
.使用 java.util.Vector
作为基础会很奇怪,因为您最终会得到两组存储,其中一组在 std::vector
中。 ,以及 java.util.Vector
中的一个.
SWIG 不为您执行此操作的原因是因为 you can't have AbstractList<double>
in Java ,它必须是 AbstractList<Double>
( Double
继承自 Object
而 double
是原始类型)。
说了这么多,我整理了一个小例子来包装 std::vector<double>
和 std::vector<std::vector<double> >
在 Java 中很好。它并不完整,但它支持 Java 和 set()
中的“for each”迭代风格。/get()
在元素上。展示如何在需要时实现其他东西就足够了。
我将在我们进行时分部分讨论接口(interface)文件,但基本上它都是顺序和完整的。
以 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 接口(interface)中使用它来使共享对象/DLL 为接口(interface)的用户透明地加载。接口(interface)文件中接下来是
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
.接下来在接口(interface)文件中,我们告诉它我们想要实现哪些基类和接口(interface),并编写一些额外的 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
是我们将在接口(interface)的 Java 端调用的 std::vector<double>
)。我们还提供了
get
的实现和 set
在可以处理 double
的 Java 端至 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
我在 Linux/x86 上使用 g++ 4.4 版和 SWIG 1.3.40 对此进行了测试。
完整版
num.i
可以找到 here ,但始终可以通过将每个部分粘贴到一个文件中来从这个答案中重建。我还没有从
AbstractList
实现的事情:add()
- 可以通过 push_back()
实现, std_vector.i 甚至尝试实现默认兼容的东西,但它不适用于 Double
对比 double
问题或匹配 AbstractList
中指定的返回类型(不要忘记增加 modCount
)remove()
- 不太适合 std::vector
在时间复杂度方面,但也不是不可能实现(同样使用 modCount
)Collection
的构造函数建议使用,但未在此处实现。可以在同一个地方实现set()
和 get()
是,但需要 $javaclassname
正确命名生成的构造函数。 size_type
-> int
转换在 size()
是理智的。 关于java - SWIG (v1.3.29) 生成的 C++ 到 Java Vector 类无法正常运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8145605/