简体   繁体   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?

In SWIG 3.0.8 there is no implementation for std::list in the C++ to Java map, only std::vector . 在SWIG 3.0.8中,C ++到Java映射中没有实现std::list实现,只有std::vector This isn't very ideal for most cases, so I was wondering if it is possible to create my own SWIG definition of std::list and how would I do so? 在大多数情况下,这不是很理想,因此我想知道是否可以创建自己的std::list SWIG定义,我将如何做?

I've written a set of typemaps that should just work for wrapping std::list nicely in Java. 我编写了一组类型映射,这些类型映射应该可以很好地用Java包装std::list They use java.util.AbstractSequentialList as a base class, so there's only ever one copy of data in existence and it works nicely as both a Java and C++ data structure. 他们使用java.util.AbstractSequentialList作为基类,因此仅存在一个数据副本,它可以很好地用作Java和C ++数据结构。 This answer is broadly an improvement and port of the same techniques I used in an older answer wrapping std::vector similarly. 这个答案大致上是对我在较旧的答案包装std::vector使用的相同技术的改进和移植。

Firstly I pulled the 'autobox' typemap out of my older answer and into a standalone file, autobox.i since I'm now reusing it quite a lot: 首先,我从以前的答案中提取了“ 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))

Then I used this in my std_list.i file below: 然后我在下面的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);
  }
}

This file implements AbstractSequentialList , which mostly just boils down to implementing ListIterator . 这个文件实现了AbstractSequentialList ,它主要归结为实现ListIterator That's somewhat fiddly because the way Java implements the concept of an iterator is somewhat different to the C++ abstraction, although not completely different. 这有点奇怪,因为Java实现迭代器概念的方式与C ++抽象有所不同,尽管并不完全不同。

Our Java implementation of ListIterator is mostly just a wrapper around an opaque C++ iterator and some glue to call in to a little extra C++ code that actually uses std::advance , std::distance and operator++ / operator-- to meet the requirements needed. 我们的ListIterator Java实现大部分只是一个不透明的C ++迭代器的包装,并需要一些胶水来调用一些额外的C ++代码,这些代码实际上使用std::advancestd::distanceoperator++ / operator--满足所需的需求。 Inside the glue are the various checks needed to make the interface safe/robust as a Java programmer expects. 胶水内部有各种检查,以确保接口安全/可靠(如Java程序员所期望的)。

The SWIG interface to std::list consists of the following major parts: std :: list的SWIG接口包括以下主要部分:

  1. Declaration of the relevant parts of std::list itself. std::list本身相关部分的声明。 (Some is private because it makes no sense to Java as anything other than an implementation detail) (有些是私有的,因为除了实现细节之外,它对Java没有任何意义)
  2. Some extra private code to let us instantiate some of the templated C++ iterator code we need when we use %template inside SWIG later on. 一些额外的私人代码,可让我们实例化稍后在SWIG中使用%template时所需的一些模板化C ++迭代器代码。
  3. Setting up imports/baseclass for std::list std::list设置import / baseclass
  4. Some helper macros to make it easier to write 'the type that Java knows an opaque handle to a C++ iterator as' and 'the type that Java thinks our container holds, including any autoboxing if needed' as they get written a lot. 一些帮助程序宏使编写“编写Java知道C ++迭代器的不透明句柄的类型”和“编写Java认为容器包含的类型,包括需要的任何自动装箱”变得更加容易。
  5. Some additional Java code for every std::list<X> we wrap: 我们包装的每个std::list<X>一些其他Java代码:

    1. Another constructor as recommended by Java collections interface for copying from one collection to another. Java集合接口建议的另一个构造函数,用于从一个集合复制到另一个集合。 (This one uses the Java iterator we created to do it fairly efficiently). (这使用我们创建的Java迭代器来相当有效地完成此工作)。
    2. An implementation of listIterator abstract method that returns an anonymous type that glues everything together to meet all of the requirements for a mutable ListIterator . listIterator抽象方法的实现,该方法返回一个匿名类型,该类型将所有内容粘合在一起,以满足可变ListIterator所有要求。

    This is wrapped inside { } with noblock turned on so that the preprocessor macros happen, but that { } doesn't get inserted into the Java which is generated. 它包装在{ } ,且未启用noblock,以便发生预处理器宏,但是{ }不会插入到生成的Java中。

    I also used this Java trick to pass data to an anonymous class during construction (but could have used the double brace magic instead). 我还使用此Java技巧在构造过程 中将 数据传递给匿名类 (但本来可以使用双括号魔术 )。

With that in place we can validate it by running writing a SWIG module, test.i: 有了这个,我们可以通过运行SWIG模块test.i来验证它:

%module test

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

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

And some actual Java to exercise it: 和一些实际的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);
  }
}

Which works as expected: 哪个按预期工作:

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

Gives: 给出:

4
HELLO
WORLD
testing
some more
A
B
C

A random note: hasNext() and forward iteration is probably faster than hasPrevious() and reverse iteration simply because it's easier to avoid the std::distance call int that case. 随机说明: hasNext()和向前迭代可能比hasPrevious()和反向迭代更快,这是因为避免在这种情况下进行std::distance调用更容易。

(Caveat: I read the Java docs on what the semantics of the ListIterator member functions should be in rather a hurry. I could have got one or more of them subtly wrong). (注意:我急忙阅读Java文档,了解ListIterator成员函数的语义应该是什么急事。我可能误解其中的一个或多个)。

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

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