33

Finally I'm able to use std::vector in python using the [] operator. The trick is to simple provide a container in the boost C++ wrapper which handles the internal vector stuff:

#include <boost/python.hpp>
#include <vector>
class world
{
    std::vector<double> myvec;

    void add(double n)
    {
        this->myvec.push_back(n);
    }

    std::vector<double> show()
    {
     return this->myvec;
    }
};

BOOST_PYTHON_MODULE(hello)
{
    class_<std::vector<double> >("double_vector")
        .def(vector_indexing_suite<std::vector<double> >())
    ;

    class_<World>("World")
     .def("show", &World::show)
        .def("add", &World::add)
    ;
 }

The other challenge is: Howto translate python lists into std::vectors? I tried to add a c++ class expecting a std::vector as parameter and added the corresponding wrapper code:

#include <boost/python.hpp>
#include <vector>
class world
{
    std::vector<double> myvec;

    void add(double n)
    {
        this->myvec.push_back(n);
    }

    void massadd(std::vector<double> ns)
    {
        // Append ns to this->myvec
    }

    std::vector<double> show()
    {
     return this->myvec;
    }
};

BOOST_PYTHON_MODULE(hello)
{
    class_<std::vector<double> >("double_vector")
        .def(vector_indexing_suite<std::vector<double> >())
    ;

    class_<World>("World")
     .def("show", &World::show)
        .def("add", &World::add)
        .def("massadd", &World::massadd)
    ;
 }

But if doing so, I end up with the following Boost.Python.ArgumentError:

>>> w.massadd([2.0,3.0])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
    World.massadd(World, list)
did not match C++ signature:
    massadd(World {lvalue}, std::vector<double, std::allocator<double> >)

Can anybody tell me how I can access python lists within my c++ function?

Thanks, Daniel

Arlaharen
  • 3,095
  • 29
  • 26
dmorlock
  • 1,993
  • 4
  • 18
  • 22

4 Answers4

32

To make your C++ method accept Python lists you should use boost::python::list

void massadd(boost::python::list& ns)
{
    for (int i = 0; i < len(ns); ++i)
    {
        add(boost::python::extract<double>(ns[i]));
    }
}
Arlaharen
  • 3,095
  • 29
  • 26
  • 1
    `boost::python::list` can be heterogeneous, and you ahve to catch exceptions from `extract`. – eudoxos Aug 10 '11 at 08:08
  • 1
    my apologies, exception from `extract` will be translated to python automatically. Sorry. – eudoxos Aug 12 '11 at 12:30
  • 11
    yeah nice and simple to do, just the boost python documentation is so poor, and operator[] is hidden away in a template object_operators several layers up the hierarchy and not obviously an operation a list. I haven't found their website documentation any more friendly. Making len() an external method isn't as cool as they think it is either. Plus is this the only way to iterate through? – CashCow Dec 17 '12 at 11:00
  • 1
    Just remember that `boost::python::extract` will automatically try to convert items for you. E.g., giving it an integer in this case will work by automatically converting it to a double (even with `.check()`). – csl Jul 13 '16 at 14:24
  • Why `boost::python::extract` incured segment fault? – GoingMyWay Sep 16 '18 at 14:31
27

Here's what I use:

#include <boost/python/stl_iterator.hpp>

namespace py = boost::python;

template< typename T >
inline
std::vector< T > to_std_vector( const py::object& iterable )
{
    return std::vector< T >( py::stl_input_iterator< T >( iterable ),
                             py::stl_input_iterator< T >( ) );
}

Should you find the input type (py::object) too liberal, feel free to specify stricter types (py::list in your case).

rdesgroppes
  • 988
  • 12
  • 11
4

Based on the above answers I created an example of accessing python lists in C++ as well as returning a python list from a C++ function:

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

namespace py = boost::python;

// dummy class
class drow{
    public:
        std::string word;
        drow(py::list words);
        py::list get_chars();
};

// example of passing python list as argument (to constructor)
drow::drow(py::list l){
    std::string w;
    std::string token;
    for (int i = 0; i < len(l) ; i++){
        token = py::extract<std::string>(l[i]);
        w += token;
    }
    this -> word = w;
}

// example of returning a python list
py::list drow::get_chars(){
    py::list char_vec;
    for (auto c : word){
        char_vec.append(c);
    }
    return char_vec;
}

// binding with python
BOOST_PYTHON_MODULE(drow){
    py::class_<drow>("drow", py::init<py::list>())
        .def("get_chars", &drow::get_chars);
}

For a build example and a test python script take a look here

Thank you Arlaharen & rdesgroppes for the pointers (pun not intended).

Andreas Grivas
  • 337
  • 4
  • 6
2

To get automatic conversion from python lists, you have to define a converter, which

  1. checks if the list is convertible to your type (i.e. that it is a sequence; additionally, you can also check whether all elements are of required type, but that can be handled in the second step as well)
  2. returns the new object, if the first step succeeded; throw exception if a sequence element is not convertible to what you need.

I can't find now anything else than my code, you can copy&paste this template (it is specialized at the end of that file for various contained types).

eudoxos
  • 18,545
  • 10
  • 61
  • 110