Feeding a Python list into a function taking in a vector with Boost Python -
i've got function signature:
function(std::vector<double> vector); and i've exposed it, doesn't take in python lists. i've looked through other answers, , involve changing function take in boost::python::lists, don't want change function. imagine can use vector_indexing_suite write simple wrapper around function, have many functions of form , rather not write wrapper every single one. there way automatically make python list->std::vector mapping occur?
there few solutions accomplish without having modify original functions.
to accomplish small amount of boilerplate code , transparency python, consider registering custom converter. boost.python uses registered converters when going between c++ , python types. converters implicitly created when creating bindings, such when class_ exports type.
the following complete example uses iterable_converter type allows registration of conversion functions python type supporting python iterable protocol. example enable conversions for:
- collection of built-in type:
std::vector<double> - 2-dimensional collection of strings:
std::vector<std::vector<std::string> > - collection of user type:
std::list<foo>
#include <iostream> #include <list> #include <vector> #include <boost/python.hpp> #include <boost/python/stl_iterator.hpp> /// @brief mockup model. class foo {}; // test functions demonstrating capabilities. void test1(std::vector<double> values) { (auto&& value: values) std::cout << value << std::endl; } void test2(std::vector<std::vector<std::string> > values) { (auto&& inner: values) (auto&& value: inner) std::cout << value << std::endl; } void test3(std::list<foo> values) { std::cout << values.size() << std::endl; } /// @brief type allows registration of conversions /// python iterable types. struct iterable_converter { /// @note registers converter python interable type /// provided type. template <typename container> iterable_converter& from_python() { boost::python::converter::registry::push_back( &iterable_converter::convertible, &iterable_converter::construct<container>, boost::python::type_id<container>()); // support chaining. return *this; } /// @brief check if pyobject iterable. static void* convertible(pyobject* object) { return pyobject_getiter(object) ? object : null; } /// @brief convert iterable pyobject c++ container type. /// /// container concept requirements: /// /// * container::value_type copyconstructable. /// * container can constructed , populated 2 iterators. /// i.e. container(begin, end) template <typename container> static void construct( pyobject* object, boost::python::converter::rvalue_from_python_stage1_data* data) { namespace python = boost::python; // object borrowed reference, create handle indicting // borrowed proper reference counting. python::handle<> handle(python::borrowed(object)); // obtain handle memory block converter has allocated // c++ type. typedef python::converter::rvalue_from_python_storage<container> storage_type; void* storage = reinterpret_cast<storage_type*>(data)->storage.bytes; typedef python::stl_input_iterator<typename container::value_type> iterator; // allocate c++ type converter's memory block, , assign // handle converter's convertible variable. c++ // container populated passing begin , end iterators of // python object container's constructor. new (storage) container( iterator(python::object(handle)), // begin iterator()); // end data->convertible = storage; } }; boost_python_module(example) { namespace python = boost::python; // register interable conversions. iterable_converter() // build-in type. .from_python<std::vector<double> >() // each dimension needs convertable. .from_python<std::vector<std::string> >() .from_python<std::vector<std::vector<std::string> > >() // user type. .from_python<std::list<foo> >() ; python::class_<foo>("foo"); python::def("test1", &test1); python::def("test2", &test2); python::def("test3", &test3); } interactive usage:
>>> import example >>> example.test1([1, 2, 3]) 1 2 3 >>> example.test1((4, 5, 6)) 4 5 6 >>> example.test2([ ... ['a', 'b', 'c'], ... ['d', 'e', 'f'] ... ]) b c d e f >>> example.test3([example.foo(), example.foo()]) 2 a few comments on approach:
- the
iterable_converter::convertiblefunction changed allowing python list, rather allowing type supports iterable protocol. however, extension may become unpythonic result. - the conversions registered based on c++ types. thus, registration needs done once, same registered conversion selected on number of exported functions accept c++ type argument.
- it not introduce unnecessary types
exampleextension namespace. - meta-programming allow multi-dimensional types recursively register each dimension type. however, example code complex enough, did not want add additional level of complexity.
alternative approaches include:
- create custom function or template function accepts
boost::python::listeach function acceptingstd::vector. approach causes bindings scale based on amount of functions being exported, rather amount of types needing converted. using boost.python
vector_indexing_suite.*_indexing_suiteclasses export type adapted match semantics of python list or dictionaries. thus, python code has know exact container type provide, resulting in less-pythonic extension. example, ifstd::vector<double>exportedvecdouble, resulting python usage be:v = example.vecdouble() v[:] = [1, 2, 3] example.test1(v)however, following not work because exact types must match, exporting class registers conversion between
vecdouble,std::vector<double>:example.test1([4, 5, 6])while approach scales types rather functions, results in less pythonic extension , bloats
examplenamespace unnecessary types.
Comments
Post a Comment