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。以下是我的界面文件的一个片段:

1
2
3
4
5
6
7
%include"std_vector.i"
namespace std {
  %template(Vector) vector<double>;
  %template(Matrix) vector<vector<double> >;
}

%include"std_string.i"

我使用的swig版本中包含了std_string.istd_vector.i。我的第一个惊喜是Java输出包含了SWIG的"自己"版本的向量类(而不是使用EDCOX1(2))。我真正的问题是,从这些函数返回的向量似乎不起作用。例如,我不能使用get()(有时会使程序崩溃)或返回负值的size()函数来检索它们的内容。我知道EDOCX1 5包含数据,因为我编码了"相同的函数"的"字符串"版本,这些函数简单地迭代通过EDCOX1×5(s)在原生C++代码中返回,并以逗号分隔的EDCOX1×7值返回内容。虽然这是一个有效的解决方案,但最终我还是希望它能与我一起正确地工作,能够接收和操作Vectors。任何帮助/小费都将不胜感激。


在Java中封装EDCOX1×0的适当基类型是EDOCX1×1。使用java.util.Vector作为基础是很奇怪的,因为最终会有两组存储,一组在std::vector中,一组在java.util.Vector中。

SWIG没有为你这样做的原因是因为你不能在Java中拥有EDCOX1×5,它必须是EDCOX1×5(EDCOX1,7)继承自EDCOX1,8,而EDCOX1(7)是原始类型。

我已经讲了一个小例子,它封装了EDCOX1、10和EDCOX1,11在Java中很好地封装。它不是完整的,但是它支持Java和EDCOX1中的"每种"迭代方式,在元素上使用12Ω/EDCOX1×13Ω。它应该足以说明如何在需要的时候实现其他事情。

我将在接下来的部分中讨论接口文件,但基本上,它都是连续的和完整的。

从定义我们的模块numnum.i开始:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
%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.
"
+ e);
      System.exit(1);
    }
  }
%}

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

在EDCOX1的18个方面也有一个窍门,我喜欢在Java SWIG接口中使用,以使共享对象/ DLL透明地为接口的用户加载。

接口文件的下一个部分是我们想要包装的std::vector的部分。我不使用std_vector.i,因为我们需要做一些更改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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::vector公开为size_impl。我们需要这样做,因为Java希望EDCOX1 OR 25返回EDCOX1,26,因为EDCOX1,0版本,返回EDCOX1,28,这可能不太可能是EDOCX1,26。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
%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;
  }

%}

这为EDOCX1的10和30的EDOCX1的一个基类设置了EDCOX1,11为32(EDCOX1,34)是我们在接口的Java端调用EDOCX1 10。

我们还提供了一个在Java端实现EDCOX1 36和EDCOX1 37的实现,它可以处理EDCOX1 7对EDCOX1、7、7的转换和返回。

最后,我们在界面中添加了:

1
2
3
4
5
6
7
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(具有特定类型)称为Vector,将std::vector >称为Matrix。我们还告诉swig公开我们的两个测试函数。

下一步,EDCOX1〔44〕,一个简单的EDCOX1(45)在Java中执行我们的代码有点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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();
    }
  }
}

要构建和运行此功能,我们需要:

1
2
3
4
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可以在这里找到,但始终可以通过将每个部分粘贴到一个文件中从这个答案中重建。

我没有从AbstractList中执行的事情:

  • add()—可以通过push_back()标准向量来实现,我甚至尝试实现默认兼容的东西,但是它不适用于DoubleDouble问题,或者与AbstractList中指定的返回类型相匹配(不要忘记增加modCount)。
  • remove()—对于std::vector来说,时间复杂度不是很好,但也不可能实现(与modCount一样)。
  • 建议使用另一个Collection的构造函数,但此处不实现。可以在同一个地方实现set()get()是,但需要$javaclassname正确命名生成的构造函数。
  • 您可能希望使用类似的方法检查size_type->int转换在size()中是否正常。

  • 我是那个在这个问题上慷慨大方的人,因为我也有同样的问题。我有点不好意思报告我终于找到了真正的解决方案——它在swig手册中!修复方法是在编译生成的代码时使用g++-fno-strict-aliasing标志——就这么简单。我不想承认,我花了很多时间才发现这一点。

    问题在于,最近版本的g++进行了一些激进的优化,这些优化对指针别名做出了假设,这些假设不适用于为std_vector生成的Swig代码(以及在其他情况下)。g++4.1没有做到这一点,但4.4.5确实做到了。这些假设是完全有效的,并且被现行的ISO标准所允许,尽管我不确定它们有多广为人知。基本上,不同类型的两个指针(除了少数例外)永远不能指向同一个地址。swig生成的用于在指向对象的指针和jlong之间转换的代码与此规则不符。