我有一些本机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
包含数据,因为我编写了相同函数的'String'版本,它们只是遍历Vector
s(返回本机C ++代码)并以逗号分隔的方式返回内容{ {1}}价值。虽然这是一个有效的解决方法,但最终我希望这能够正常工作,因为我能够接收和操纵String
。任何帮助/提示将非常感激。
答案 0 :(得分:15)
在Java中包装std::vector
的适当基类型是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
是原始类型。)
说了所有我把一个小例子放在一起的小例子,它很好地包装了Java std::vector<double>
和std::vector<std::vector<double> >
。它不完整,但它支持Java中的“for each”样式和元素上的set()
/ get()
。它应该足以显示如何在需要时实现其他内容。
我会在接下来的部分详细介绍接口文件,但基本上它都是顺序完整的。
从定义我们的模块num.i
的{{1}}开始:
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
个和用于测试的两个函数实现(它们可能位于一个单独的文件中,我只是将它们放在懒惰/便利中)。
我还希望在Java SWIG接口中使用num_wrap.cxx
,以便为接口用户透明地加载共享对象/ DLL。
接口文件中的下一步是我们要包装的%pragma(java) jniclasscode=
部分。我没有使用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;
}
}
};
}
,它告诉SWIG将%rename(size_impl) size;
从size()
公开为std::vector
。我们需要这样做是因为Java希望size_impl
返回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端。)。
我们还在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公开我们的两个测试函数。
接下来,test.java
,Java中的一个简单的main
来运用我们的代码:
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
vs double
问题或匹配AbstractList
中指定的返回类型(不要忘记增加modCount
)remove()
- 在时间复杂度方面对std::vector
不太好,但也不是不可能实现(同样使用modCount
)Collection
的构造函数,但不在此处实现。可以在set()
和get()
的同一位置实现,但需要$javaclassname
才能正确命名生成的构造函数。size_type
中的int
- &gt; size()
转换是否合理。 答案 1 :(得分:4)
我是在这个问题上提供赏金的人,因为我有同样的问题。我有点尴尬地报告说我终于找到了真正的解决方案 - 而且它在SWIG手册中!修复是在编译生成的代码时使用-fno-strict-aliasing
g++
标志 - 这很简单。我不愿意承认,谷歌终于发现了这一点。
问题是最新版本的g++
做了一些积极的优化,这些优化使得对于代码SWIG不具有的指针别名的假设为std_vector
生成(在其他情况下)。{{ 1}} 4.1不会这样做,但4.4.5肯定会这样做。这些假设是完全有效的,并且符合当前的ISO标准,尽管我不确定它们是多么出名。基本上,不同类型的两个指针(有一些例外)可以从不指向同一个地址。 SWIG生成的用于在指针到对象和g++
之间进行转换的代码违反了这条规则。