当使用SWIG和C ++的std :: map时,没有用于Java的迭代器 [英] No iterator for Java when using SWIG with C++'s std::map

查看:315
本文介绍了当使用SWIG和C ++的std :: map时,没有用于Java的迭代器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

解决方案

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

从你的问题,不清楚是否在地图中使用什么类型,如果你想能够遍历对(如在C ++中),键或值。



第一件事,SWIG接口文件的前导我用来测试这个:

/ p>

 %模块测试

%includestd_string.i
%includestd_map。 i

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

 %typemap(javainterfaces)MapIteratorjava.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 ,它有一个私人的实现,除了异常抛出 next()和迭代器所需的状态在 std :: map 的自己 const_iterator )。

 %javamethodmodifiers MapIterator :: nextImplprivate; 
%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&地图;
};
%}



最后,我们需要告诉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 (autoboxing我相信是术语),除非你决定wrap对已经在这种情况下,你可以与原始成员配对。


I have implemented a class with std::map in C++ and created interface using SWIG to be call from Java. However there is no iterator object that allows me to iterate through the entries in the SWIG wrapped std::map. Does anyone know how to create an iterator?

解决方案

In order to be able to iterate over an Object in Java it needs to implement Iterable. This in turn requires a member function called iterator() which returns a suitable implementation of an Iterator.

From your question it's not quite clear if what types you're using in the map and if you want to be able to iterate over the pairs (as you would in C++), the keys or the values. The solutions to the three variants are substantially similar, my example below picked the values.

First things first, the preamble for the SWIG interface file I used to test this:

%module test

%include "std_string.i"
%include "std_map.i"

In order to implement the iterable map I've declared, defined and wrapped another class in the SWIG interface file. This class, MapIterator implements the Iterator interface for us. It's a mixture of both Java and wrapped C++, where one was easier than the other to write. Firstly some Java, a typemap that gives the interface it implements and then two of the three methods required for the Iterable interface, given as a typemap:

%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();
  }
%}

Then we supply the C++ part of MapIterator, which has a private implementation of all but the exception throwing part of next() and the state needed for the iterator (expressed in terms of std::map's own 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;    
  };
%}

Finally we need to tell SWIG that the std::map we're wrapping implements the Iterable interface and provide an extra member function for the purposes of wrapping std::map which returns a new instance of the MapIterator class we just wrote:

%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>;

This could be more generic, with macros for example to hide the types of the map such that if you have multiple maps it's just a question of "calling" the macro for the appropriate maps just like you do with %template.

There's also a slight complication with maps of primitive types - you'll need to arrange for the Java side to use Double/Integer instead of double/int (autoboxing I believe is the term), unless you decided to wrap pairs already in which case you could make a pair with primitive members.

这篇关于当使用SWIG和C ++的std :: map时,没有用于Java的迭代器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆