Я немного новичок в SWIG и C++, так что это может быть очевидно, но я не могу понять это. У меня есть объект коллекции C++ (в MWE, Pantry
), который в C++ индексируется std::string
, но выполняет итерации по объектам. Это работает на C++, используя цикл, основанный на диапазоне, с реализациями begin()
и end()
.Как создать целевую языковой объект с итерацией объекта SWIG C++ без использования индекса
Вот MWE:
pantry.i
%module(directors="1") my_collection
%include "stdint.i"
%include "std_string.i"
%include "std_shared_ptr.i"
%include "std_vector.i"
%{
#include "my_collection.cpp"
%}
%nodefaultctor;
%shared_ptr(Food);
%include "my_collection.cpp"
pantry.cpp
#pragma once
#include <string>
#include <vector>
#include <map>
#include <memory>
#include <sstream>
#ifdef SWIG
%define TO_STRING()
%extend {
%feature("python:slot", "tp_repr", functype="reprfunc") to_string;
}
std::string to_string()
%enddef
%define SUBSCRIPT(return_type, subscript_type)
%feature("python:slot", "mp_subscript", functype="binaryfunc") __getitem__;
%extend {
return_type __getitem__(subscript_type key) {
return (*($self))[key];
};
};
return_type operator[](subscript_type key)
%enddef
#else
#define TO_STRING() \
std::string to_string()
#define SUBSCRIPT(return_type, subscript_type) \
return_type operator[](subscript_type key)
#endif
class Food {
public:
std::string name;
Food(std::string name) {
this->name = name;
};
TO_STRING() {
std::ostringstream stream;
stream << "<Food " << this->name << ">";
return stream.str();
};
};
class Pantry {
private:
// _foods is not grammatically correct
std::map<std::string, std::shared_ptr<Food>> _food_map;
std::vector<std::shared_ptr<Food>> _food_vec;
public:
Pantry() {
};
std::shared_ptr<Food> add(std::string name) {
auto food = std::shared_ptr<Food>(new Food(name));
this->_food_map[food->name] = food;
this->_food_vec.push_back(food); // p.s., how do I prevent making a copy?
return food;
};
SUBSCRIPT(std::shared_ptr<Food>, std::string) {
return this->_food_map.at(key);
};
TO_STRING() {
return "<Pantry>";
};
std::vector<std::shared_ptr<Food>>::const_iterator begin() {
return this->_food_vec.begin();
};
std::vector<std::shared_ptr<Food>>::const_iterator end() {
return this->_food_vec.end();
};
size_t size() {
return this->_food_vec.size();
};
};
Я также положить вместе короткий скрипт Python:
import pantry
pant = mc.Pantry()
print(pant.add('Bacon'))
print(pant.add('Cheese'))
print('subscript[Bacon]: {}'.format(pant['Bacon']))
for food in pant:
print('iter: ... {}'.format(food))
Затем, в зависимости от того, прошли ли -builtin
флага Swig, я получаю ошибку:
- без
-builtin
:TypeError: in method 'Pantry___getitem__', argument 2 of type 'std::string'
- , который имеет смысл, потому что я выбрал индексировать строки - с
-builtin
:TypeError: 'my_collection.Pantry' object is not iterable
- Я думаю, что это вызвано тем, что SWIG не неявно создавал методы, которые я не объявлял, что кажется правильным в этом примере.
Как я могу перебирать содержимое коллекции без явного вызова метода? Я понимаю, что могу просто сделать публикацию _food_vec
и перебрать ее, но мой фактический код более сложный, и я бы предпочел не делать этого. Аналогично, я мог бы реализовать и другой метод .iterate()
, но я уже сделал работу с begin()
и end()
.
Edit: Что бы целевого языка агностик метод, который я мог бы реализовать похож на макросы, которые я сделал для TO_STRING()
и SUBSCRIPT
, и то, что было бы правильным шаблон для добавления в файл интерфейса (что-то вроде %template(FoodIterator) std::vector<Food>::iterator;
?
Итератор, возвращаемый '__iter __()', должен также иметь метод '__next __()', чтобы убедиться, что он работает как в 2, так и в 3 – Alec
. Я думаю, что # 2 - единственный вариант, поскольку я намеренно проиндексирован строками , Я думаю, вы правы, что мне нужно реализовать метод '__getitem __()', но я изо всех сил пытаюсь понять, как это сделать агностическим образом на целевом языке.Я также добавил объяснение ошибок или, по крайней мере, мое понимание их. – SimplyKnownAsG
Есть еще одна вещь. В Python экземпляр итератора не зависит от его экземпляра контейнера, так, например, когда вы пишете код типа 'for food in Pantry()', экземпляр 'Pantry' будет создан и удален сразу после внутреннего вызова' Pantry .__ iter __() '. Таким образом, если ваша реализация итератора C++ просто содержит 'std :: map :: iterator', тогда вы получите segfault. Тем не менее я вижу единственный безопасный и простой способ сделать вашу коллекцию итерируемой при копировании основной коллекции и позволить SWIG генерировать итератор для вас. –