SWIG(v1.3.29)生成C ++到Java Vector类不能正常工作 [英] SWIG (v1.3.29) generated C++ to Java Vector class not acting properly
问题描述
我有一些本地C ++代码,我使用SWIG转换为Java,以便我的Java应用程序可以使用它。特别是有一些函数返回std :: vector。以下是我的介面档案片段:
%includestd_vector.i
namespace std {
%template(Vector)vector< double>
%template(Matrix)vector< vector< double> > ;;
}
%includestd_string.i
code> std_string.i 和 std_vector.i
包含在我的SWIG建立中。我的第一个惊喜是,Java输出包括SWIG的Vector类的自己版本(而不是使用 java.util.Vector
)。我真正的问题是从这些函数返回的Vectors不似乎工作。例如,我不能使用 get()
(有时会崩溃程序)或 size()
函数返回负值。我知道 Vector
包含数据,因为我编码的String版本的相同的函数,它简单地遍历 Vector
s(返回原生C ++代码),并以逗号分隔的 String
值返回内容。虽然这是一个有效的解决方法,最终我希望这能正常工作,我能够接收和操纵 Vectors
。
包装的适当基本类型 std :: vector
在Java中是 java.util.AbstractList
。使用 java.util.Vector
作为基础是奇怪的,因为你会得到两组存储,一个在 std :: vector
和 java.util.Vector
中的一个。
SWIG的原因不这样做是因为您不能拥有在Java中的AbstractList< double>
必须 AbstractList< Double>
( Double
继承自 Object
,而 double
是原始类型)。
说完所有我把一个小例子,包装 std :: vector< double>
和 std :: vector< std :: vector< double> >
很好用Java。它不完整,但它支持Java和 set()中的for each风格的迭代。
/ get()
on元素。
我会在接下来的章节中讨论接口文件,但基本上是这样的
从 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 );
} catch(UnsatisfiedLinkError e){
System.err.println(本机代码库加载失败。\\\
+ e);
System.exit(1);
}
}
%}
$ c> #include s用于生成的 num_wrap.cxx
和两个测试函数的实现(它们可以在一个单独的文件中
%pragma(java)jniclasscode =
我想在Java SWIG接口中使用,以使共享对象/ DLL为接口的用户透明地加载。
接口文件中的下一个是我们想要包装 std :: vector
的部分。我不使用 std_vector.i
,因为我们需要进行一些更改:
namespace std {
template< class T>类向量{
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){
//如果需要,将抛出,swig将处理
return self- (一世);
}
void set_impl(int i,const value_type& val)throw(std :: out_of_range){
//可以抛出
self> at val;
}
}
};
}
这里的主要变化是%rename(size_impl) size;
,它告诉SWIG将 size()
从 std :: vector
size_impl
。我们需要这样做,因为Java希望 size
返回 int
,其中 std: :vector
版本返回 size_type
,这很可能不会是 int
。
接下来在接口文件中我们告诉它我们想要实现的基类和接口,以及编写一些额外的Java代码来强制不兼容类型的函数之间的事情: / p>
%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>
for std :: vector< double>
和 java .util.AbstractList< Vector>
for std :: vector< std :: vector< double> >
(向量
是我们将要调用的 std :: vector< double>
在接口的Java端)。
我们还提供 get
和 set
在Java端可以处理 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& code>(具体类型)为
Vector
,类似 std :: vector< vector< double> >
为 Matrix
。我们还告诉SWIG公开我们的两个测试函数。
接下来, test.java
c $ c> main 在Java中执行我们的代码:
import java.util。摘要:
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();
}
}
}
do:
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测试
我在Linux / x86上使用g ++ 4.4和SWIG 1.3.40测试了这个。 p>
可以找到 num.i
的完整版本这里,但是总是可以通过将每个部分一起粘贴到一个文件中来从该答案重构。
我从没有实现的事情AbstractList
:
-
add()
- 可以通过push_back()
实现,std_vector.i甚至尝试在默认情况下实现兼容的东西,与Double
vs问题或匹配
AbstractList $ c中指定的返回类型$ c>(不要忘记增加
modCount
) -
remove()
- 不适合std :: vector
在时间复杂度方面,但不是不可能实现(同样modCount
) - 建议使用另一个
Collection
的构造函数,但不在此处实现。可以在同一个地方实现set()
和get()
都是,但需要$ javaclassname
可正确命名生成的构造函数。 - 您可能需要使用这检查
size_type
- >int
转换size()
是正常的。
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.
The appropriate base type for wrapping std::vector
in Java is java.util.AbstractList
. Using java.util.Vector
as a base would be odd because you'd end up with two sets of storage, one in the std::vector
, and one in the java.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 be AbstractList<Double>
(Double
inherits from Object
whereas double
is a primitive type).
Having said all that I've put together a small example that wraps std::vector<double>
and std::vector<std::vector<double> >
nicely in Java. It's not complete, but it supports the "for each" style of iteration in Java and set()
/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 module 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);
}
}
%}
We have #include
s for the generated num_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 using std_vector.i
because we need to make a few changes:
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;
}
}
};
}
The main change here is %rename(size_impl) size;
, which tells SWIG to expose size()
from std::vector
as size_impl
instead. We need to do this because Java expects size
to return an int
where as the std::vector
version returns a size_type
which more than likely won't be int
.
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:
%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;
}
%}
This sets a base class of java.util.AbstractList<Double>
for std::vector<double>
and java.util.AbstractList<Vector>
for std::vector<std::vector<double> >
(Vector
is what we will be calling std::vector<double>
on the Java side of the interface).
We also supply an implementation of get
and set
on the Java side which can handle the double
to Double
conversion and back again.
Lastly in the interface we add:
namespace std {
%template(Vector) std::vector<double>;
%template(Matrix) std::vector<vector<double> >;
}
std::vector<double> testVec();
std::vector<std::vector<double> > testMat();
This tells SWIG to refer to std::vector<double>
(with the specific type) as Vector
and similarly for std::vector<vector<double> >
as Matrix
. We also tell SWIG to expose our two test functions.
Next up, test.java
, a simple main
in Java to exercise our code a little:
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();
}
}
}
To build and run this we do:
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
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
)- A constructor which takes another
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. - You might want to use something like this to check that the
size_type
->int
conversion insize()
is sane.
这篇关于SWIG(v1.3.29)生成C ++到Java Vector类不能正常工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!