你如何在SWIG中为C ++中的stg :: list <std :: string>创建一个类型映射到List <string>?

时间:2016-04-11 14:52:55

标签: java c++ swig

在SWIG 3.0.8中,C ++到Java地图中没有std::list的实现,只有std::vector。对于大多数情况,这不是很理想,所以我想知道是否可以创建我自己的std::list的SWIG定义,我该怎么做?

1 个答案:

答案 0 :(得分:0)

我编写了一组类型映射,它们只适用于在Java中很好地包装std::list。他们使用java.util.AbstractSequentialList作为基类,因此只存在一个数据副本,它作为Java和C ++数据结构很好地工作。这个答案大体上是我在an older answer wrapping std::vector中使用的相同技术的改进和移植。

首先,我将“autobox”类型图从我的旧答案中删除并放入一个独立文件autobox.i中,因为我现在正在重复使用它:

// Java typemaps for autoboxing in return types of generics
%define AUTOBOX(CTYPE, JTYPE)
%typemap(autobox) CTYPE, const CTYPE&, CTYPE& "JTYPE"
%enddef
AUTOBOX(double, Double)
AUTOBOX(float, Float)
AUTOBOX(boolean, Boolean)
AUTOBOX(signed char, Byte)
AUTOBOX(short, Short)
AUTOBOX(int, Integer)
AUTOBOX(long, Long)
AUTOBOX(SWIGTYPE, $typemap(jstype,$1_basetype))

然后我在下面的std_list.i文件中使用了这个:

%include <autobox.i>
%include <stdint.i>

%{
#include <list>
#include <algorithm>
%}

namespace std {
  template <typename T> class list {
  public:
    // This typedef is a weird hack to make stuff work
    typedef std::list<T>::iterator iterator;
    typedef size_t size_type;
    typedef T value_type;
    typedef T& reference;

    void assign(size_type n, const value_type &val);

    bool empty() const;

    list(size_type n, const value_type &value=value_type());
    list(const list &o);
    list();
    ~list();

    size_type max_size () const;

    void pop_back();
    void pop_front();
    void push_back(const value_type &x);
    void push_front(const value_type &x);
    void remove(const T &v);

    // Possible bug: jint != size_type
    jint size () const;
    void sort();

%javamethodmodifiers "private";
    // Only for helping implement listIterator
    iterator begin();
    iterator insert(iterator pos, const value_type &v);

    %extend {
      static void set(iterator pos, const value_type& v) {
        *pos = v;
      }

      jint previous_index(const iterator& pos) const {
        return pos == self->begin() ? -1 : std::distance(self->begin(), static_cast<std::list<T>::const_iterator>(pos));
      }

      jint next_index(const iterator& pos) const {
        return pos == self->end() ? self->size() : std::distance(self->begin(), static_cast<std::list<T>::const_iterator>(pos));
      }

      static iterator next(iterator pos) {
        return ++pos;
      }

      static iterator previous(iterator pos) {
        return --pos;
      }

      static value_type deref(const iterator& pos) {
        return *pos;
      }

      static void advance(iterator& pos, jint index) {
        std::advance(pos, index);
      }

      bool has_next(const iterator& pos) const {
        return pos != $self->end();
      }
    }
%javamethodmodifiers "public";
  };
}

%typemap(javaimports) std::list %{
  import java.util.AbstractSequentialList;
  import java.util.ListIterator;
  import java.util.NoSuchElementException;
  import java.util.Collection;
%}

%typemap(javabase) std::list "AbstractSequentialList<$typemap(autobox,$1_basetype::value_type)>"

#define JAVA_VALUE_TYPE $typemap(autobox,$1_basetype::value_type)
#define JAVA_ITERATOR_TYPE $typemap(jstype, $1_basetype::iterator)

%typemap(javacode,noblock=1) std::list {
  public $javaclassname(Collection c) {
    this();
    ListIterator<JAVA_VALUE_TYPE> it = listIterator(0);
    for (Object o: c) {
      it.add((JAVA_VALUE_TYPE)o);
    }
  }

  public ListIterator<JAVA_VALUE_TYPE> listIterator(int index) {
    return new ListIterator<JAVA_VALUE_TYPE>() {
      private JAVA_ITERATOR_TYPE pos;
      private JAVA_ITERATOR_TYPE last;

      private ListIterator<JAVA_VALUE_TYPE> init(int index) {
        pos = $javaclassname.this.begin();
        $javaclassname.advance(pos, index);
        return this;
      }

      public void add(JAVA_VALUE_TYPE v) {
        // Technically we can invalidate last here, but this makes more sense
        last=$javaclassname.this.insert(pos, v);
      }

      public void set(JAVA_VALUE_TYPE v) {
        if (null==last) {
          throw new IllegalStateException();
        }
        $javaclassname.set(last, v);
      }

      public void remove() {
        if (null==last) {
          throw new IllegalStateException();
        }
        $javaclassname.this.remove(last);
        last=null;
      }

      public int previousIndex() {
        return $javaclassname.this.previous_index(pos);
      }

      public int nextIndex() {
        return $javaclassname.this.next_index(pos);
      }

      public JAVA_VALUE_TYPE previous() {
        if (previousIndex() < 0) {
          throw new NoSuchElementException();
        }
        last = pos;
        pos = $javaclassname.previous(pos);
        return $javaclassname.deref(last);
      }

      public JAVA_VALUE_TYPE next() {
        if (!hasNext()) {
          throw new NoSuchElementException();
        }
        last = pos;
        pos = $javaclassname.next(pos);  
        return $javaclassname.deref(last);
      }

      public boolean hasPrevious() {
        return previousIndex() != -1;
      }

      public boolean hasNext() { 
        return $javaclassname.this.has_next(pos);
      }
    }.init(index);
  }
}

此文件实现AbstractSequentialList,这主要归结为实施ListIterator。这有点繁琐,因为Java实现迭代器概念的方式与C ++抽象有些不同,尽管并不完全不同。

我们ListIterator的Java实现主要只是一个不透明的C ++迭代器的包装器,并且可以调用一些额外的C ++代码来实际使用std::advancestd::distance和{ {1}} / operator++以满足所需的要求。在胶水内部是各种检查,以使接口安全/健壮,正如Java程序员所期望的那样。

std :: list的SWIG接口包含以下主要部分:

  1. 宣布operator--本身的相关部分。 (有些是私有的,因为除了实现细节之外,Java没有任何意义)
  2. 一些额外的私有代码,让我们实例化我们稍后在SWIG中使用std::list时需要的一些模板化C ++迭代器代码。
  3. %template
  4. 设置导入/基类
  5. 一些辅助宏可以更容易地编写'Java知道C ++迭代器的不透明句柄的类型'和'Java认为我们的容器持有的类型,包括任何需要的自动装箱',因为它们写得很多
  6. 我们打包的每个std::list的一些额外Java代码:

    1. Java集合接口推荐的另一个构造函数,用于从一个集合复制到另一个集合。 (这个使用我们创建的Java迭代器来相当有效地完成它。)
    2. std::list<X>抽象方法的实现,它返回一个匿名类型,将所有内容粘合在一起以满足可变listIterator的所有要求。
    3. ListIterator内部打开noblock,以便预处理器宏发生,但{ }不会插入到生成的Java中。

      我也使用了此Java trick to pass data to an anonymous class during construction(但可能已使用the double brace magic代替。)

      有了这个,我们可以通过编写SWIG模块test.i:

      来验证它
      { }

      和一些实际的Java来练习它:

      %module test
      
      %include "std_list.i"
      %include <std_string.i>
      
      %template(DoubleList) std::list<double>;
      %template(StringList) std::list<std::string>;
      

      按预期工作:

      import java.util.ArrayList;
      
      public class run {
        public static void dump(java.util.AbstractCollection c) {
          for (Object o: c) {
            System.out.println(o);
          }
        }
      
        public static void main(String[] argv) {
          System.loadLibrary("test");
          for (int i = 0; i < 1; ++i) {
            go();
      //      System.gc();
          }
        }
      
        public static void go() {
          StringList sl = new StringList();
          dump(sl);
          sl.add(0,"HELLO"); // 1 arg form also worked
          sl.add(1,"WORLD");
          sl.add(2,"testing");
          sl.add(3,"some more");
          System.out.println(sl.size());
          dump(sl);
      
          sl = new StringList(new ArrayList<String>() {{
            add("A");
            add("B");
            add("C");
          }});
          dump(sl);
        }
      }
      

      给出:

      swig3.0 -java -c++ -Wall test.i 
      javac *.java 
      g++ -Wall -Wextra -shared -o libtest.so test_wrap.cxx -I/usr/lib/jvm/default-java/include/ -I/usr/lib/jvm/default-java/include/linux -fPIC  -D_GLIBCXX_DEBUG
      LD_LIBRARY_PATH=. java run
      

      随机说明:4 HELLO WORLD testing some more A B C 和前向迭代可能比hasNext()快,反向迭代只是因为在这种情况下更容易避免hasPrevious()调用。

      (警告:我阅读了关于ListIterator成员函数的语义应该是匆忙的Java文档。我可能有一个或多个巧妙地错了。)