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

时间:2011-11-16 01:40:55

标签: java c++ vector java-native-interface swig

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

2 个答案:

答案 0 :(得分:15)

在Java中包装std::vector的适当基类型是java.util.AbstractList。使用java.util.Vector作为基础会很奇怪,因为您最终会得到两组存储空间,一组存储在std::vector中,另一组存储在java.util.Vector中。

SWIG不会为您执行此操作的原因是因为you can't have AbstractList<double> in Java,它必须是AbstractList<Double>Double继承自Objectdouble是原始类型。)

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

我会在接下来的部分详细介绍接口文件,但基本上它都是顺序完整的。

从定义我们的模块num.i的{​​{1}}开始:

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); } } %} 提供了#include个和用于测试的两个函数实现(它们可能位于一个单独的文件中,我只是将它们放在懒惰/便利中)。

我还希望在Java SWIG接口中使用num_wrap.cxx,以便为接口用户透明地加载共享对象/ DLL。

接口文件中的下一步是我们要包装的%pragma(java) jniclasscode=部分。我没有使用std::vector,因为我们需要进行一些更改:

std_vector.i

此处的主要更改是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; } } }; } ,它告诉SWIG将%rename(size_impl) size;size()公开为std::vector。我们需要这样做是因为Java希望size_impl返回sizeint版本返回std::vectorsize_type很可能不会int

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

%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>设置std::vector<double>的基类,为java.util.AbstractList<Vector>设置std::vector<std::vector<double> >Vector是我们将要调用的std::vector<double>接口的Java端。)。

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

最后在我们添加的界面中:

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>(具体类型)称为Vector,将std::vector<vector<double> >称为Matrix。我们还告诉SWIG公开我们的两个测试函数。

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

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();
    }
  }
}

要构建并运行它,我们会这样做:

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的完整版本可以找到here,但总是可以通过将每个部分粘贴到一个文件中来从此答案重建。

我未从AbstractList实施的内容:

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

答案 1 :(得分:4)

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

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