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

sir*_*rlo 11 c++ java java-native-interface swig vector

我有一些本机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"
Run Code Online (Sandbox Code Playgroud)

std_string.istd_vector.i被列入我的,我使用痛饮的构建.我的第一个惊喜是Java输出包含了SWIG的"自有"版本的Vector类(与使用相反java.util.Vector).我真正的问题是从这些函数返回的Vector似乎不起作用.例如,我无法使用get()(有时崩溃程序)或size()返回负值的函数来检索其内容.我知道Vectors包含数据,因为我编写了相同函数的'String'版本,它们只是遍历Vectors(回到本机C++代码中)并以逗号分隔String值返回内容.虽然这是一个有效的解决方法,但最终我希望这能够正常工作,因为我能够接收和操纵Vectors.任何帮助/提示将非常感激.

Fle*_*exo 15

std::vector用Java 包装的适当基类型是java.util.AbstractList.使用java.util.Vector,因为你有两套存储,一个在最终的基地将是奇数std::vector,还有一个在java.util.Vector.

SWIG不会为你做这件事的原因是因为你不能拥有AbstractList<double>Java,它必须是AbstractList<Double>(从而Double继承Objectdouble不是原始类型).

说完所有我已经把一个小例子包装好std::vector<double>并且std::vector<std::vector<double> >很好地用Java编写.它并不完整,但它支持Java和set()/或get()元素中的"for each"迭代方式.它应该足以显示如何在需要时实现其他内容.

我将继续讨论接口文件,但基本上它们都是顺序和完整的.

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("num");
    } catch (UnsatisfiedLinkError e) {
      System.err.println("Native code library failed to load. \n" + e);
      System.exit(1);
    }
  }
%}
Run Code Online (Sandbox Code Playgroud)

我们有用于测试#include的生成num_wrap.cxx和两个函数实现(它们可能在一个单独的文件中,我只是把它们放在懒惰/方便中).

还有一个技巧%pragma(java) jniclasscode=,我喜欢在Java SWIG接口中使用,以便为接口的用户透明地加载共享对象/ DLL.

接口文件中的下一步是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;
            }
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

这里是主要的变化%rename(size_impl) size;,它告诉SWIG暴露size()std::vector作为size_impl代替.我们需要这样做,因为Java的期望size返回一个int为地方std::vector版本返回size_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;
  }

%}
Run Code Online (Sandbox Code Playgroud)

这设置了java.util.AbstractList<Double>for std::vector<double>java.util.AbstractList<Vector>for 的基类std::vector<std::vector<double> >(Vector我们将std::vector<double>在接口的Java端调用).

我们还供应的实现get,并set在Java端,可以处理doubleDouble转换,然后再返回.

最后在界面中我们添加:

namespace std {
  %template(Vector) std::vector<double>;
  %template(Matrix) std::vector<vector<double> >;
}

std::vector<double> testVec();
std::vector<std::vector<double> > testMat();
Run Code Online (Sandbox Code Playgroud)

这告诉SWIG引用std::vector<double>(具有特定类型)as Vector和类似的std::vector<vector<double> >as Matrix.我们还告诉SWIG公开我们的两个测试函数.

接下来,test.java简单地main用Java来练习我们的代码:

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();
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

为了构建和运行它,我们做到:

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
Run Code Online (Sandbox Code Playgroud)

我在Linux/x86上使用g ++版本4.4和SWIG 1.3.40对此进行了测试.

num.i可在此处找到完整版本,但始终可以通过将每个部分粘贴到一个文件中来从此答案重建.

我没有实现的事情AbstractList:

  1. add()- 可以通过实现push_back(),std_vector.i甚至尝试在默认情况下实现兼容的东西,但它不能与Doublevs double问题一起使用或匹配指定的返回类型AbstractList(不要忘记增加modCount)
  2. remove()- std::vector在时间复杂性方面不是很好,但也不是不可能实现(同样modCount)
  3. Collection建议使用另一个构造函数,但这里没有实现.可以在同一个地方执行set()get()是的,但需要$javaclassname正确地命名生成的构造函数.
  4. 您可能希望使用类似这样的内容来检查size_type- > int转换size()是否合理.