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