簡體   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