SWIG (v1.3.29) 生成的 C++ 到 Java Vector 类无法正常运行 [英] SWIG (v1.3.29) generated C++ to Java Vector class not acting properly

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

问题描述

我有一些本机 C++ 代码,我正在使用 SWIG 将它们转换为 Java,以便我的 Java 应用程序可以使用它.特别是有一些函数返回 std::vector.这是我的界面文件的片段:

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

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.

推荐答案

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

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.

SWIG 不为您执行此操作的原因是因为 您不能Java中有AbstractList,必须是AbstractList(Double继承自Objectdouble 是原始类型).

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).

说了这么多,我整理了一个小例子,它包装了 std::vectorstd::vector;> 在 Java 中很好.它并不完整,但它支持 Java 中的for each"迭代样式和元素上的 set()/get().展示如何在需要时/在需要时实施其他事情就足够了.

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.

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

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. 
" + e);
      System.exit(1);
    }
  }
%}

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

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).

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

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.

接口文件中接下来是我们要包装的 std::vector 部分.我没有使用 std_vector.i 因为我们需要做一些改变:

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

这里的主要变化是 %rename(size_impl) size;,它告诉 SWIG 从 std::vector 公开 size()作为 size_impl 代替.我们需要这样做是因为 Java 期望 size 返回一个 intstd::vector 版本返回一个 size_type 很可能不是 int.

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.

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

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

%}

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

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).

我们还在Java端提供了getset的实现,可以处理doubleDouble> 转换并再次返回.

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

这告诉 SWIG 将 std::vector(具有特定类型)引用为 Vector,对于 std::vector 作为 Matrix.我们还告诉 SWIG 公开我们的两个测试函数.

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.

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

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

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

I tested this with g++ version 4.4 and SWIG 1.3.40 on Linux/x86.

num.i 的完整版本可以在 在这里,但总是可以通过将每个部分粘贴到一个文件中来从这个答案中重建.

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.

我没有从 AbstractList 实现的东西:

Things I've not implemented from 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() 中的 size_type->int 转换是合理的.
  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天全站免登陆