SWIG(v1.3.29)生成的C ++到Java Vector类不能正常运行

我有一些本机C ++代码,我正在使用SWIG转换为Java,以便我的Java应用程序可以使用它。 特别是有一些函数返回std :: vector。 这是我的界面文件的片段:

%include "std_vector.i" namespace std { %template(Vector) vector; %template(Matrix) vector<vector >; } %include "std_string.i" 

std_string.istd_vector.i包含在我正在使用的SWIG构建中。 我的第一个惊喜是Java输出包含了SWIG的“自有”版本的Vector类(而不是使用java.util.Vector )。 我真正的问题是从这些函数返回的Vector似乎不起作用。 例如,我无法使用get() (有时会使程序崩溃)或size()函数返回负值来检索其内容。 我知道Vector包含数据,因为我编写了相同函数的’String’版本,它们简单地遍历Vector (返回原生C ++代码)并以逗号分隔的String值返回内容。 虽然这是一个有效的解决方法,但最终我希望这能够正常工作,因为我能够接收和操作Vectors 。 任何帮助/提示将不胜感激。

在Java java.util.AbstractList包装std::vector的适当基类型是java.util.AbstractList 。 使用java.util.Vector作为基础将是奇怪的,因为你最终会有两组存储,一组在std::vector ,一组在java.util.Vector

SWIG不为你做这个的原因是因为你不能在Java中使用AbstractList ,它必须是AbstractListDoubleinheritance自Objectdouble是基本类型)。

说完所有我把一个小例子放在一起,在Java中很好地包装了std::vectorstd::vector > 。 它不完整,但它支持Java中的“for each”样式和元素上的set() / get() 。 它应该足以显示如何在需要时实现其他内容。

我将逐步介绍接口文件,但基本上它都是顺序和完整的。

num.i开始,它定义了我们的模块num

 %module num %{ #include  #include  std::vector testVec() { return std::vector(10,1.0); } std::vector > testMat() { return std::vector >(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); } } %} 

我们为生成的num_wrap.cxx提供了#include s和两个用于测试的函数实现(它们可以放在一个单独的文件中,我只是把它们放在懒惰/方便的地方)。

%pragma(java) jniclasscode=还有一个技巧,我喜欢在Java SWIG接口中使用,以便为接口的用户透明地加载共享对象/ DLL。

接口文件中的下一步是我们要包装的std::vector的部分。 我没有使用std_vector.i因为我们需要进行一些更改:

 namespace std { template 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将std::vector size()改为size_impl 。 我们需要这样做,因为Java期望size返回一个int ,因为std::vector版本返回的size_type很可能不会是int

接下来在接口文件中,我们告诉它我们想要实现什么基类和接口,以及编写一些额外的Java代码来强制不兼容类型的函数之间的事情:

 %typemap(javabase) std::vector "java.util.AbstractList" %typemap(javainterface) std::vector "java.util.RandomAccess" %typemap(javacode) std::vector %{ 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 > "java.util.AbstractList" %typemap(javainterface) std::vector > "java.util.RandomAccess" %typemap(javacode) std::vector > %{ 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设置java.util.AbstractList的基类,为std::vector java.util.AbstractList std::vector >Vector是我们的将在接口的Java端调用std::vector

我们还在Java端提供了getset的实现,它可以处理doubleDouble转换,然后再返回。

最后在界面中我们添加:

 namespace std { %template(Vector) std::vector; %template(Matrix) std::vector >; } std::vector testVec(); std::vector > testMat(); 

这告诉SWIG将std::vector (具有特定类型)引用为Vector并将std::vector >引用为Matrix 。 我们还告诉SWIG公开我们的两个测试函数。

接下来, test.java ,Java中的一个简单main来运用我们的代码:

 import java.util.AbstractList; public class test { public static void main(String[] argv) { Vector v = num.testVec(); AbstractList 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实现的东西:

  1. add() – 可以通过push_back() ,std_vector.i甚至尝试默认实现兼容的东西,但它不适用于Double vs double问题或者匹配AbstractList指定的返回类型(不要忘记增量modCount
  2. remove() – 在时间复杂度方面对std::vector不太好,但也不是不可能实现(同样使用modCount
  3. 建议使用另一个Collection构造函数,但不在此处实现。 可以在相同的地方实现set()get() ,但是需要$javaclassname来正确命名生成的构造函数。
  4. 您可能希望使用类似这样的东西来检查size_type – > int转换的size()是否合理。

我是在这个问题上提供赏金的人,因为我有同样的问题。 我有点尴尬地报告说我终于找到了真正的解决方案 – 而且它在SWIG手册中! 修复是在编译生成的代码时使用g++-fno-strict-aliasing标志 – 就这么简单。 我不愿意承认,谷歌花了很多时间才终于发现了这一点。

问题是g++最新版本做了一些积极的优化,这些优化使得对于代码SWIG不能为std_vector生成的指针别名做出假设(在其他情况下) std_vector g++ 4.1不会这样做,但4.4.5肯定确实。 这些假设是完全有效的,并且符合当前的ISO标准,尽管我不确定它们是多么出名。 基本上,它是两个不同类型的指针(有一些例外) 永远不会指向相同的地址。 SWIG生成的用于在指针到对象和jlong之间进行转换的代码违反了这条规则。