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.i
和std_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
,它必须是AbstractList
( Double
inheritance自Object
而double
是基本类型)。
说完所有我把一个小例子放在一起,在Java中很好地包装了std::vector
和std::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端提供了get
和set
的实现,它可以处理double
到Double
转换,然后再返回。
最后在界面中我们添加:
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
实现的东西:
-
add()
– 可以通过push_back()
,std_vector.i甚至尝试默认实现兼容的东西,但它不适用于Double
vsdouble
问题或者匹配AbstractList
指定的返回类型(不要忘记增量modCount
) -
remove()
– 在时间复杂度方面对std::vector
不太好,但也不是不可能实现(同样使用modCount
) - 建议使用另一个
Collection
构造函数,但不在此处实现。 可以在相同的地方实现set()
和get()
,但是需要$javaclassname
来正确命名生成的构造函数。 - 您可能希望使用类似这样的东西来检查
size_type
– >int
转换的size()
是否合理。
我是在这个问题上提供赏金的人,因为我有同样的问题。 我有点尴尬地报告说我终于找到了真正的解决方案 – 而且它在SWIG手册中! 修复是在编译生成的代码时使用g++
的-fno-strict-aliasing
标志 – 就这么简单。 我不愿意承认,谷歌花了很多时间才终于发现了这一点。
问题是g++
最新版本做了一些积极的优化,这些优化使得对于代码SWIG不能为std_vector
生成的指针别名做出假设(在其他情况下) std_vector
g++
4.1不会这样做,但4.4.5肯定确实。 这些假设是完全有效的,并且符合当前的ISO标准,尽管我不确定它们是多么出名。 基本上,它是两个不同类型的指针(有一些例外) 永远不会指向相同的地址。 SWIG生成的用于在指针到对象和jlong
之间进行转换的代码违反了这条规则。