我有一些本机 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)。我真正的问题是从这些函数返回的向量似乎不起作用。例如,我无法使用 get()
(有时会使程序崩溃)或返回负值的 size()
函数检索它们的内容。我知道 Vector
包含数据,因为我编写了相同函数的“String”版本,这些函数只是迭代 Vector
(回到本机 C++ 代码中)并返回以逗号分隔的 String
值中的内容。虽然这是一个有效的解决方法,但最终我希望它能够正常工作,让我能够接收和操作向量
。任何帮助/提示将不胜感激。
I have some native C++ code that I'm converting to Java using SWIG so that my Java application can use it. In particular there are some functions that return std::vector. Here's a snippet of my interface file:
%include "std_vector.i"
namespace std {
%template(Vector) vector<double>;
%template(Matrix) vector<vector<double> >;
}
%include "std_string.i"
std_string.i
and std_vector.i
were included in my build of SWIG I'm using. My first surprise was that the Java output included SWIG's "own" version of the Vector class (as opposed to using java.util.Vector
). My real issue is that the Vectors that get returned from these functions do not seem to work. For example I cannot retrieve their contents using get()
(sometimes crashing the program) or the size()
function returning negative values. I know the Vector
s contain data because I coded 'String' versions of the same functions which simply iterate through the Vector
s (back in the native C++ code) and return the contents in a comma separated String
value. While this is a valid workaround, ultimately I would like this to work properly with me being able to receive and manipulate the Vectors
. Any helps/tips would be much appreciated.
发布评论
评论(2)
在 Java 中包装
std::vector
的适当基类型是java.util.AbstractList
。使用 java.util.Vector 作为基础会很奇怪,因为您最终会得到两组存储,一组在 std::vector 中,一组在java.util.Vector
。SWIG 不为您执行此操作的原因是因为您不能拥有
AbstractList
在 Java 中,它必须是AbstractList
(Double
继承自Object
,而double
是原始类型)。说了这么多,我整理了一个小例子,其中包含
std::vector
和std::vector 。 >
在 Java 中表现得很好。它并不完整,但它支持 Java 中的“foreach”迭代风格以及元素上的set()
/get()
。它应该足以展示如何在您需要时实现其他事情。我将分部分讨论接口文件,但基本上它都是连续且完整的。
从定义我们的模块
num
的num.i
开始:我们为生成的
num_wrap.cxx
提供了#include
以及用于测试的函数的两个实现(它们可以在一个单独的文件中,我只是出于懒惰/方便将它们放在这里)。我喜欢在 Java SWIG 接口中使用
%pragma(java) jniclasscode=
来实现共享对象/DLL 为接口用户透明地加载。接口文件中的下一个部分是我们想要包装的 std::vector 部分。我没有使用
std_vector.i
因为我们需要进行一些更改:这里的主要更改是
%rename(size_impl) size;
,它告诉 SWIG 公开 <将std::vector
中的 code>size() 改为size_impl
。我们需要这样做,因为 Java 期望size
返回一个int
,而std::vector
版本返回一个size_type 很可能不是
int
。我们告诉它我们想要实现什么基类和接口,并编写一些额外的 Java 代码来强制类型不兼容的函数之间的事物:
接下来在接口文件中, Double> 为
std::vector
和java.util.AbstractList
为std::向量 >
(Vector
是我们在接口的 Java 端调用的std::vector
)。我们还在 Java 端提供了
get
和set
的实现,可以处理double
到Double
的转换以及又回来了。最后在接口中我们添加:
这告诉 SWIG 将
std::vector
(具有特定类型)引用为Vector
,对于std 也类似: :向量<向量<双> >
作为矩阵
。我们还告诉 SWIG 公开我们的两个测试函数。接下来,
test.java
,一个简单的 Java 中的main
,用于稍微练习一下我们的代码:要构建并运行它,我们需要这样做:
我使用 g++ 版本 4.4 和 SWIG 对其进行了测试Linux/x86 上的 1.3.40。
num.i
的完整版本可以在此处找到a>,但始终可以通过将每个部分粘贴到一个文件中来根据此答案进行重建。我没有从
AbstractList
实现的东西:add()
- 可以通过push_back()
实现,std_vector.i 甚至尝试实现一些东西默认情况下兼容,但它不适用于Double
与double
问题或匹配AbstractList
中指定的返回类型(不要忘记增加modCount
)remove()
- 就时间复杂度而言,对于std::vector
来说不是很好,但也不是不可能实现(与modCount
类似>)Collection
,但这里没有实现。可以在set()
和get()
的同一位置实现,但需要$javaclassname
来正确命名生成的构造函数。size()
中的 >size_type->int
转换是合理的。The appropriate base type for wrapping
std::vector
in Java isjava.util.AbstractList
. Usingjava.util.Vector
as a base would be odd because you'd end up with two sets of storage, one in thestd::vector
, and one in thejava.util.Vector
.The reason SWIG doesn't do this for you though is because you can't have
AbstractList<double>
in Java, it has to beAbstractList<Double>
(Double
inherits fromObject
whereasdouble
is a primitive type).Having said all that I've put together a small example that wraps
std::vector<double>
andstd::vector<std::vector<double> >
nicely in Java. It's not complete, but it supports the "for each" style of iteration in Java andset()
/get()
on elements. It should be sufficient to show how to implement other things as/when you want them.I'll talk through the interface file in sections as we go, but basically it'll all be sequential and complete.
Starting with
num.i
which defines our modulenum
:We have
#include
s for the generatednum_wrap.cxx
and two implementations of functions for testing (they could be in a separate file I just put them here out of laziness/convenience).There's also a trick there with the
%pragma(java) jniclasscode=
that I like to use in Java SWIG interfaces to cause the shared object/DLL to be loaded transparently for the user of the interface.Next up in the interface file is the parts of
std::vector
we want to wrap. I'm not usingstd_vector.i
because we need to make a few changes:The main change here is
%rename(size_impl) size;
, which tells SWIG to exposesize()
fromstd::vector
assize_impl
instead. We need to do this because Java expectssize
to return anint
where as thestd::vector
version returns asize_type
which more than likely won't beint
.Next up in the interface file we tell it what base class and interfaces we want to implement as well as writing some extra Java code to coerce things between functions with incompatible types:
This sets a base class of
java.util.AbstractList<Double>
forstd::vector<double>
andjava.util.AbstractList<Vector>
forstd::vector<std::vector<double> >
(Vector
is what we will be callingstd::vector<double>
on the Java side of the interface).We also supply an implementation of
get
andset
on the Java side which can handle thedouble
toDouble
conversion and back again.Lastly in the interface we add:
This tells SWIG to refer to
std::vector<double>
(with the specific type) asVector
and similarly forstd::vector<vector<double> >
asMatrix
. We also tell SWIG to expose our two test functions.Next up,
test.java
, a simplemain
in Java to exercise our code a little:To build and run this we do:
I tested this with g++ version 4.4 and SWIG 1.3.40 on Linux/x86.
The complete version of
num.i
can be found here, but can always be reconstructed from this answer by pasting each of the part together into one file.Things I've not implemented from
AbstractList
:add()
- can be implemented viapush_back()
, std_vector.i even tries to implement something compatible by default, but it doesn't work with theDouble
vsdouble
problem or match the return type specified inAbstractList
(Don't forget to incrementmodCount
)remove()
- not great forstd::vector
in terms of time complexity, but not impossible to implement either (likewise withmodCount
)Collection
is recommended, but not implemented here. Can be implemented at the same placeset()
andget()
are, but will need$javaclassname
to name the generated constructor correctly.size_type
->int
conversion insize()
is sane.我是在这个问题上提供赏金的人,因为我也遇到了同样的问题。我有点尴尬地告诉大家,我终于找到了真正的解决方案——它就在 SWIG 手册中!修复方法是在编译生成的代码时使用
g++
的-fno-strict-aliasing
标志 - 就这么简单。我不愿承认,我花了很多时间谷歌搜索才最终发现这一点。问题在于,最新版本的 g++ 做了一些激进的优化,这些优化对指针别名做出了一些假设,而这些假设不适用于 SWIG 为 std_vector 生成的代码(以及其他情况下的代码)。 )
g++
4.1 不会这样做,但 4.4.5 肯定会这样做。这些假设是完全有效的,并且是当前 ISO 标准所允许的,尽管我不确定它们的知名度如何。基本上,不同类型的两个指针(有一些例外)永远不能指向同一个地址。 SWIG 生成的用于在对象指针和 jlong 之间进行转换的代码违反了此规则。I'm the person who offered the bounty on this question because I had the same issue. I'm a bit embarrassed to report that I've finally found the real solution -- and it's in the SWIG manual! The fix is to use the
-fno-strict-aliasing
flag forg++
when compiling the generated code -- simple as that. I hate to admit that it took a lot of Googling to finally found this out.The problem is that recent versions of
g++
do some aggressive optimizations that make assumptions about pointer aliasing that don't hold for the code SWIG generates forstd_vector
(and in other cases.)g++
4.1 doesn't do this, but 4.4.5 definitely does. The assumptions are perfectly valid and allowed by the current ISO standard, although I'm not sure how well known they are. Basically, it's that two pointers of different types (with a few exceptions) can never point to the same address. The code that SWIG generates to convert between pointer-to-object andjlong
falls afoul of this rule.