boost-python

Making a boost python array work with the for loop


I am trying to have an array using boost python

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

class MyObject
{
public:
    MyObject(int value)
        : value_(value)
    {
    }

    int value() const { return value_; }
    void set_value(int value) { value_ = value; }

private:
    int value_;
};

class MyArray
{
public:
    MyArray(int size)
        : array_(size)
    {
        for (auto& obj : array_)
        {
            obj = std::make_shared<MyObject>(0);
        }
    }

    std::shared_ptr<MyObject>& operator[](int index) { return array_[index]; }
    int size() const { return static_cast<int>(array_.size()); }

private:
    std::vector<std::shared_ptr<MyObject>> array_;
};

BOOST_PYTHON_MODULE(example)
{
    namespace python = boost::python;

    python::class_<MyObject, std::shared_ptr<MyObject>>("MyObject", python::init<int>())
        .add_property("value", &MyObject::value, &MyObject::set_value);

    python::class_<MyArray, std::shared_ptr<MyArray>>("MyArray", python::init<int>())
        .def("__getitem__", &MyArray::operator[], boost::python::return_value_policy<boost::python::copy_non_const_reference>())
        .def("__len__", &MyArray::size);
}

It works reasonably well in Python: the len function works, and the accessor [] works as well but the for loop does not stop at the right iterator and I get a C++ run time error, trying to access myArray[3], the fourth (inexistant) item

my_array = analyse_script.MyArray(3)

my_array[0].value = 1
my_array[1].value = 2
my_array[2].value = 3

print(len(my_array))  # prints "3"

for obj in my_array:
  print(obj.value)  # prints "1", "2", "3"

What should I change to make it work? Thanks in advance!


Solution

  • In the end rather than working with an unnecessary class MyArray, I was advised by a colleague to use vector_indexing_suite

    So my code looks like this and works

        boost::python::class_<std::vector<std::shared_ptr<MyObject>>>("MyArray")
            .def(boost::python::vector_indexing_suite<std::vector<std::shared_ptr<MyObject>>>())
            .def("__iter__", boost::python::iterator<std::vector<std::shared_ptr<MyObject>>>())
            ;
    
    

    I am still interested to understand why my first attempt did not work but this is less urgent!