将 SWIG 与 C++ 的 std::map 一起使用时没有 Java 迭代器

No iterator for Java when using SWIG with C++'s std::map

本文关键字:迭代器 Java map SWIG C++ std 一起      更新时间:2023-10-16

我在C++中用std::map实现了一个类,并使用SWIG创建了接口,以便从Java调用。但是,没有一个迭代器对象允许我遍历SWIG封装的std::map中的条目。有人知道如何创建迭代器吗?

为了能够在Java中迭代对象,它需要实现Iterable。这又需要一个名为iterator()的成员函数,该函数返回Iterator的适当实现。

从你的问题来看,还不太清楚你在映射中使用的是什么类型,以及你是否希望能够迭代对(就像在C++中一样)、键还是值。这三种变体的解决方案基本相似,下面的示例选择了值。

首先,SWIG接口文件的前导码我用来测试这个:

%module test
%include "std_string.i"
%include "std_map.i"

为了实现可迭代映射,我在SWIG接口文件中声明、定义并包装了另一个类。这个类MapIterator为我们实现了Iterator接口。它是Java和封装C++的混合体,其中一个比另一个更容易编写。首先是一些Java,一个给出其实现的接口的类型映射,然后是Iterable接口所需的三种方法中的两种,作为类型映射给出:

%typemap(javainterfaces) MapIterator "java.util.Iterator<String>"
%typemap(javacode) MapIterator %{
  public void remove() throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }
  public String next() throws java.util.NoSuchElementException {
    if (!hasNext()) {
      throw new java.util.NoSuchElementException();
    }
    return nextImpl();
  }
%}

然后,我们提供了MapIterator的C++部分,除了next()的异常抛出部分和迭代器所需的状态(用std::map自己的const_iterator表示)之外,它对所有内容都有一个私有实现。

%javamethodmodifiers MapIterator::nextImpl "private";
%inline %{
  struct MapIterator {
    typedef std::map<int,std::string> map_t;
    MapIterator(const map_t& m) : it(m.begin()), map(m) {}
    bool hasNext() const {
      return it != map.end();
    }
    const std::string& nextImpl() {
      const std::pair<int,std::string>& ret = *it++;
      return ret.second;
    }
  private:
    map_t::const_iterator it;
    const map_t& map;    
  };
%}

最后,我们需要告诉SWIG,我们正在包装的std::map实现了Iterable接口,并为包装std::map提供了一个额外的成员函数,该函数返回了我们刚刚编写的MapIterator类的一个新实例:

%typemap(javainterfaces) std::map<int,std::string> "Iterable<String>"
%newobject std::map<int,std::string>::iterator() const;
%extend std::map<int,std::string> {
  MapIterator *iterator() const {
    return new MapIterator(*$self);
  }
}
%template(MyMap) std::map<int,std::string>;

这可能更通用,例如,使用宏来隐藏贴图的类型,这样,如果您有多个贴图,则只需要像使用%template一样,为适当的贴图"调用"宏。

基元类型的映射也有一点复杂——您需要安排Java端使用Double/Integer,而不是double/int(我认为自动装箱是一个术语),除非您已经决定包装对,在这种情况下,您可以用基元成员组成一对。