2015-12-27 2 views
0

Я немного новичок в 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;?

ответ

0

Вы two options реализовать итерацию интерфейс:.

  1. Implement __iter__(), который возвращает экземпляр объекта, имеющего next()
  2. __getitem__() который принимает integers or slice objects.

Я думаю, это должно объяснить обе ошибки, а также дать понять, как исправить.

+0

Итератор, возвращаемый '__iter __()', должен также иметь метод '__next __()', чтобы убедиться, что он работает как в 2, так и в 3 – Alec

+0

. Я думаю, что # 2 - единственный вариант, поскольку я намеренно проиндексирован строками , Я думаю, вы правы, что мне нужно реализовать метод '__getitem __()', но я изо всех сил пытаюсь понять, как это сделать агностическим образом на целевом языке.Я также добавил объяснение ошибок или, по крайней мере, мое понимание их. – SimplyKnownAsG

+0

Есть еще одна вещь. В Python экземпляр итератора не зависит от его экземпляра контейнера, так, например, когда вы пишете код типа 'for food in Pantry()', экземпляр 'Pantry' будет создан и удален сразу после внутреннего вызова' Pantry .__ iter __() '. Таким образом, если ваша реализация итератора C++ просто содержит 'std :: map :: iterator', тогда вы получите segfault. Тем не менее я вижу единственный безопасный и простой способ сделать вашу коллекцию итерируемой при копировании основной коллекции и позволить SWIG генерировать итератор для вас. –

 Смежные вопросы

  • Нет связанных вопросов^_^