Нет итератора для Java при использовании SWIG с std::map в C++

Я реализовал класс с std::map в C++ и создал интерфейс, используя SWIG для вызова из Java. Однако нет объекта итератора, который позволил бы мне перебирать записи в SWIG std::map, Кто-нибудь знает, как создать итератор?

1 ответ

Решение

Для того, чтобы иметь возможность перебирать объект в 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();
  }
%}

Затем мы поставляем часть C++ MapIterator, которая имеет частную реализацию всех, кроме исключения, бросая часть 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 (я полагаю, это автобокс), если только вы не решили обернуть пары уже, в этом случае вы могли бы создать пару с примитивными членами.

Другие вопросы по тегам