Нет итератора для Java при использовании SWIG с std :: map в C ++ - PullRequest
8 голосов
/ 27 февраля 2012

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

1 Ответ

12 голосов
/ 28 февраля 2012

Чтобы иметь возможность перебирать объект в 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() и состояния, необходимого для итератора (выраженного через собственные const_iterator std::map).

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

...