Boost :: python и итераторы из виртуальных функций базового класса - PullRequest
2 голосов
/ 21 ноября 2011

Итак, я пытаюсь написать базовый класс для обработки классов, которые обертывают std :: vector, и я работаю над определением функции __iter__.Мой первый подход, который я хотел бы получить, состоит в том, чтобы в базовом классе были функции begin () и end ().Это хорошо компилируется, но когда я запускаю код на python, я получаю сообщение об ошибке, похожее на:

Boost.Python.ArgumentError: Типы аргументов Python в

Контейнер .__ iter __ (Контейнер)

не соответствует сигнатуре C ++:

__ iter __ (boost :: python :: back_reference > std :: allocator >>> &>)

Следующее примерное расширение можно протестировать с помощью

from test import *

c = Container()

for i in range(10):
    c.append(K(str(i)))

for i in c:
    print i

Расширение образца:

#include <memory>
#include <vector>
#include <string>
#include <boost/python.hpp>

template<class T> 
T* get_pointer(std::shared_ptr<T> const& p) { return p.get(); }

template<class T>
class stl_iter {
protected:
    typedef typename T::value_type V;
    typedef typename T::iterator iter;
    virtual T& get_vector()=0;
public:
    virtual ~stl_iter() {}

    virtual void append(V item) {
        get_vector().push_back(item);
    }

    virtual iter begin() {
        return get_vector().begin();
    }

    virtual iter end() {
        return get_vector().end();
    }
};

class K {
    std::string val;
public:
    K(std::string s) : val(s) {}

    std::string get_val() const { return val; }
};
typedef std::shared_ptr<K> pK;
typedef std::vector<pK> vK;

class Container : public stl_iter<vK> {
    vK items;
protected:
    vK& get_vector() { return items; }

public:
    // Works if I uncomment these
    //vK::iterator begin() { return get_vector().begin(); }
    //vK::iterator end() { return get_vector().end(); }


public:
    virtual ~Container() {}
};
typedef std::shared_ptr<Container> pContainer;
typedef std::vector<pContainer> vContainer;

BOOST_PYTHON_MODULE_INIT(test) {
    using namespace boost::python;

    class_<K, pK>("K", init<std::string>())
        .def("__str__", &K::get_val)
        ;

    class_<Container, pContainer>("Container")
        .def("append", &Container::append)
        .def("__iter__", range(&Container::begin, &Container::end))
        ;
}

Ответы [ 2 ]

1 голос
/ 11 января 2014

range() также можно использовать нормально, но в любом случае лежащий в основе итератор должен поддерживать семантику STL, здесь образец :

...
.def("__iter__"
     , range<return_value_policy<copy_non_const_reference> >(
           &my_sequence<heavy>::begin
         , &my_sequence<heavy>::end))
1 голос
/ 02 июня 2012

На самом деле ответ был довольно прост: заставить контейнер вести себя как контейнер stl, а затем использовать iterator<>() функцию boost вместо range().

Чтобы сделать это, мне пришлось сделать typedefsв stl_iter public и переименовать их в value_type и iterator.

Вот обновленный код C ++.

#include <memory>
#include <vector>
#include <string>
#include <boost/python.hpp>

template<class T> 
T* get_pointer(std::shared_ptr<T> const& p) { return p.get(); }

template<class T>
class stl_iter {
protected:
    virtual T& get_vector()=0;
public:
    // Next two lines changed, and made public
    typedef typename T::value_type value_type;
    typedef typename T::iterator iterator;

    virtual ~stl_iter() {}

    virtual void append(value_type item) {
        get_vector().push_back(item);
    }

    virtual iterator begin() {
        return get_vector().begin();
    }

    virtual iterator end() {
        return get_vector().end();
    }
};

class K {
    std::string val;
public:
    K(std::string s) : val(s) {}

    std::string get_val() const { return val; }
};
typedef std::shared_ptr<K> pK;
typedef std::vector<pK> vK;

class Container : public stl_iter<vK> {
    vK items;
protected:
    vK& get_vector() { return items; }

public:
    virtual ~Container() {}
};
typedef std::shared_ptr<Container> pContainer;
typedef std::vector<pContainer> vContainer;

BOOST_PYTHON_MODULE_INIT(test) {
    using namespace boost::python;

    class_<K, pK>("K", init<std::string>())
        .def("__str__", &K::get_val)
        ;

    class_<Container, pContainer>("Container")
        .def("append", &Container::append)
        // Use iterator() instead of range()
        .def("__iter__", iterator<Container>())
        ;
}
...