繁体   English   中英

您如何为std :: list创建类型图 <std::string> 列出 <String> 在C ++中在SWIG中转换为Java?

[英]How would you create a typemap for std::list<std::string> to List<String> in C++ to Java in SWIG?

在SWIG 3.0.8中,C ++到Java映射中没有实现std::list实现,只有std::vector 在大多数情况下,这不是很理想,因此我想知道是否可以创建自己的std::list SWIG定义,我将如何做?

我编写了一组类型映射,这些类型映射应该可以很好地用Java包装std::list 他们使用java.util.AbstractSequentialList作为基类,因此仅存在一个数据副本,它可以很好地用作Java和C ++数据结构。 这个答案大致上是对我在较旧的答案包装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::distanceoperator++ / operator--满足所需的需求。 胶水内部有各种检查,以确保接口安全/可靠(如Java程序员所期望的)。

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

  1. std::list本身相关部分的声明。 (有些是私有的,因为除了实现细节之外,它对Java没有任何意义)
  2. 一些额外的私人代码,可让我们实例化稍后在SWIG中使用%template时所需的一些模板化C ++迭代器代码。
  3. std::list设置import / baseclass
  4. 一些帮助程序宏使编写“编写Java知道C ++迭代器的不透明句柄的类型”和“编写Java认为容器包含的类型,包括需要的任何自动装箱”变得更加容易。
  5. 我们包装的每个std::list<X>一些其他Java代码:

    1. Java集合接口建议的另一个构造函数,用于从一个集合复制到另一个集合。 (这使用我们创建的Java迭代器来相当有效地完成此工作)。
    2. listIterator抽象方法的实现,该方法返回一个匿名类型,该类型将所有内容粘合在一起,以满足可变ListIterator所有要求。

    它包装在{ } ,且未启用noblock,以便发生预处理器宏,但是{ }不会插入到生成的Java中。

    我还使用此Java技巧在构造过程 中将 数据传递给匿名类 (但本来可以使用双括号魔术 )。

有了这个,我们可以通过运行SWIG模块test.i来验证它:

%module test

%include "std_list.i"
%include <std_string.i>

%template(DoubleList) std::list<double>;
%template(StringList) std::list<std::string>;

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

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()和反向迭代更快,这是因为避免在这种情况下进行std::distance调用更容易。

(注意:我急忙阅读Java文档,了解ListIterator成员函数的语义应该是什么急事。我可能误解其中的一个或多个)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM