java - SWIG (v1.3.29) 生成的 C++ 到 Java Vector 类无法正常运行

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

我有一些 native 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 s 包含数据,因为我编码了相同函数的“字符串”版本,这些函数只是遍历 Vector s(回到 native C++ 代码中)并返回以逗号分隔的内容 String值(value)。虽然这是一个有效的解决方法,但最终我希望它能够正常工作,我能够接收和操作 Vectors .任何帮助/提示将不胜感激。

最佳答案

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

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

说了这么多,我整理了一个小例子来包装 std::vector<double>std::vector<std::vector<double> >在 Java 中很好。它并不完整,但它支持 Java 和 set() 中的“for each”迭代风格。/get()在元素上。展示如何在需要时实现其他东西就足够了。

我将在我们进行时分部分讨论接口(interface)文件,但基本上它都是顺序和完整的。

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

我们有 #include s 用于生成的 num_wrap.cxx和两个用于测试的函数实现(它们可以在一个单独的文件中,我只是出于懒惰/方便将它们放在这里)。
%pragma(java) jniclasscode= 还有一个技巧。我喜欢在 Java SWIG 接口(interface)中使用它来使共享对象/DLL 为接口(interface)的用户透明地加载。

接口(interface)文件中接下来是 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;
            }
        }
    };
}

这里的主要变化是%rename(size_impl) size; ,它告诉 SWIG 暴露 size()来自 std::vectorsize_impl反而。我们需要这样做,因为 Java 期望 size返回 int其中 std::vector版本返回 size_type这很可能不会是 int .

接下来在接口(interface)文件中,我们告诉它我们想要实现哪些基类和接口(interface),并编写一些额外的 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 是我们将在接口(interface)的 Java 端调用的 std::vector<double>)。

我们还提供了 get 的实现和 set在可以处理 double 的 Java 端至 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> (具体类型)为 Vectorstd::vector<vector<double> > 类似如 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();
    }
  }
}

为了构建和运行它,我们这样做:
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 实现的事情:
  • add() - 可以通过 push_back() 实现, std_vector.i 甚至尝试实现默认兼容的东西,但它不适用于 Double对比 double问题或匹配 AbstractList 中指定的返回类型(不要忘记增加 modCount )
  • remove() - 不太适合 std::vector在时间复杂度方面,但也不是不可能实现(同样使用 modCount )
  • 一个接受另一个 Collection 的构造函数建议使用,但未在此处实现。可以在同一个地方实现set()get()是,但需要 $javaclassname正确命名生成的构造函数。
  • 你可能想使用类似 this 的东西检查 size_type -> int转换在 size()是理智的。
  • 关于java - SWIG (v1.3.29) 生成的 C++ 到 Java Vector 类无法正常运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8145605/

    相关文章:

    java - 如何在抛出异常后促使应用程序继续运行?

    java - 如何从套接字中找出 url 请求?

    java - 如何在网页中流式传输视频?

    c++ - 如何让 Qt Creator 在 Mac M1 上使用 Rosetta 和 x86 编译器?

    r - 组合/求和 R 中整数向量中的两个位置

    java - Hazelcast 持久消息队列

    c++ - 如何有效地计算给定数字的所有不同组合的按位异或值之和?

    c++ - 在QT中为MacO和Windows创建安装程序的过程

    c++ - 逐行读取文本文件会使 vector 为空

    c++ - vector 内存分配策略