SWIG(v1.3.29)生成C ++到Java Vector类不能正常工作 [英] SWIG (v1.3.29) generated C++ to Java Vector class not acting properly

查看:151
本文介绍了SWIG(v1.3.29)生成C ++到Java Vector类不能正常工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些本地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


  1. add() - 可以通过 push_back()实现,std_vector.i甚至尝试在默认情况下实现兼容的东西,与 Double vs 问题或匹配 AbstractList (不要忘记增加 modCount

  2. remove() - 不适合 std :: vector 在时间复杂度方面,但不是不可能实现(同样 modCount

  3. 建议使用另一个 Collection 的构造函数,但不在此处实现。可以在同一个地方实现 set() get()都是,但需要 $ javaclassname 可正确命名生成的构造函数。

  4. 您可能需要使用检查 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 Vectors contain data because I coded 'String' versions of the same functions which simply iterate through the Vectors (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 #includes 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:

  1. add() - can be implemented via push_back(), std_vector.i even tries to implement something compatible by default, but it doesn't work with the Double vs double problem or match the return type specified in AbstractList (Don't forget to increment modCount)
  2. remove() - not great for std::vector in terms of time complexity, but not impossible to implement either (likewise with modCount)
  3. A constructor which takes another Collection is recommended, but not implemented here. Can be implemented at the same place set() and get() are, but will need $javaclassname to name the generated constructor correctly.
  4. You might want to use something like this to check that the size_type->int conversion in size() is sane.

这篇关于SWIG(v1.3.29)生成C ++到Java Vector类不能正常工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆