c++ - Boost.Python custom converter -
i have class taking vector parameter (a binary file content).
i convert python 'str' type vector of unsigned char 1 of class method.
boost_python_module(hello) { class_<hello>("hello"). // method takes string parameter , print .def("printchar", &hello::printchar) // method takes vector<unsigned char> parameter .def("storefile", &hello::storefile) } using custom converter seems need if modify boost::python::converter::registry modify calls printchar , python methods passing string parameter converted vector.
how can register per-method converter ?
there 2 approaches problem:
- export helper function
hello.storefileacceptsboost::python::str, constructsstd::vector<unsigned char>string, , delegates c++hello::storefilemember function. - write custom converter. while converters cannot registered on per-function basis, scoped not perform unintended conversions. approach provides more reusability.
helper function
using helper function not affect other exported function. thus, conversion between python string , std::vector<unsigned char> occur hello.storefile.
void hello_storefile(hello& self, boost::python::str str) { std::cout << "hello_storefile" << std::endl; // obtain handle string. const char* begin = pystring_asstring(str.ptr()); // delegate hello::storefile(). self.storefile(std::vector<unsigned char>(begin, begin + len(str))); } ... boost_python_module(hello) { namespace python = boost::python; python::class_<hello>("hello") // method takes string parameter , print .def("printchar", &hello::printchar) // method takes vector<unsigned char> parameter .def("storefile", &hello_storefile) ; } custom converter
a converter registration has 3 parts:
- a function checks if
pyobjectconvertible. return ofnullindicatespyobjectcannot use registered converter. - a construct function constructs c++ type
pyobject. function called ifconverter(pyobject)not returnnull. - the c++ type constructed.
therefore, given c++ type, if converter(pyobject) returns non-null value, construct(pyobject) create c++ type. c++ type serves key registry, boost.python should not perform unintended conversions.
in context of question, want converter std::vector<unsigned char> converter(pyobject) returns non-null if pyobject pystring, , converter(pyobject) use pyobject create , populate std::vector<unsigned char>. conversion occur if exported c++ functions have std::vector<unsigned char> (or const reference) parameter , argument provided python string. therefore, custom converter not affect exported functions have std::string parameters.
here complete example. have opted make converter generic allow multiple types constructable python string. chaining support, should have same feel other boost.python types.
#include <iostream> #include <list> #include <string> #include <vector> #include <boost/foreach.hpp> #include <boost/python.hpp> class hello { public: void printchar(const std::string& str) { std::cout << "printchar: " << str << std::endl; } void storefile(const std::vector<unsigned char>& data) { std::cout << "storefile: " << data.size() << ": "; boost_foreach(const unsigned char& c, data) std::cout << c; std::cout << std::endl; } }; /// @brief type allows conversions of python strings // vectors. struct pystring_converter { /// @note registers converter python interable type /// provided type. template <typename container> pystring_converter& from_python() { boost::python::converter::registry::push_back( &pystring_converter::convertible, &pystring_converter::construct<container>, boost::python::type_id<container>()); return *this; } /// @brief check if pyobject string. static void* convertible(pyobject* object) { return pystring_check(object) ? object : null; } /// @brief convert pystring container. /// /// container concept requirements: /// /// * container::value_type copyconstructable char. /// * 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; // 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. const char* begin = pystring_asstring(object); data->convertible = new (storage) container( begin, // begin begin + pystring_size(object)); // end } }; boost_python_module(hello) { namespace python = boost::python; // register pystring conversions. pystring_converter() .from_python<std::vector<unsigned char> >() .from_python<std::list<char> >() ; python::class_<hello>("hello") // method takes string parameter , print .def("printchar", &hello::printchar) // method takes vector<unsigned char> parameter .def("storefile", &hello::storefile) ; } and example usage:
>>> hello import hello >>> h = hello() >>> h.printchar('abc') printchar: abc >>> h.storefile('def') storefile: 3: def >>> h.storefile([c c in 'def']) traceback (most recent call last): file "<stdin>", line 1, in <module> boost.python.argumenterror: python argument types in hello.storefile(hello, list) did not match c++ signature: storefile(hello {lvalue}, std::vector<unsigned char, std::allocator<unsigned char> >) for more on custom converters , c++ containers, consider reading this answer.
Comments
Post a Comment