Boost.Python自定义转换器

时间:2013-04-19 13:16:12

标签: c++ python boost binding boost-python

我有一个类将vector作为参数(二进制文件内容)。

我想将python'st'类型转换为unsigned char的向量,但仅适用于我的一个类方法。

BOOST_PYTHON_MODULE(hello) {  class_<Hello>("Hello").
     // This method takes a string as parameter and print it
     .def("printChar", &Hello::printChar)
     // This method takes a vector<unsigned char> parameter
     .def("storeFile", &Hello::storeFile) }

使用自定义转换器似乎是我需要的但是如果我修改我的boost :: python :: converter :: registry它将修改我对printChar的所有调用以及所有传递字符串的python方法将参数转换为vector 。

如何注册per-method转换器?

1 个答案:

答案 0 :(得分:16)

这个问题有两种方法:

  • 将助手函数导出为Hello.storeFile,接受boost::python::str,从字符串构造std::vector<unsigned char>,并委托给C ++ Hello::storeFile成员函数。
  • 编写自定义转换器。虽然转换器无法基于每个功能进行注册,但它们的范围非常合理,因为它们不会执行任何意外的转换。这种方法通常提供更多的可重用性。

辅助功能

使用辅助函数不会影响任何其他导出函数。因此,python字符串和std::vector<unsigned char>之间的转换只会发生在Hello.storeFile

void Hello_storeFile(Hello& self, boost::python::str str)
{
  std::cout << "Hello_storeFile" << std::endl;
  // Obtain a handle to the string.
  const char* begin = PyString_AsString(str.ptr());
  // Delegate to Hello::storeFile().
  self.storeFile(std::vector<unsigned char>(begin, begin + len(str)));
}

...

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

  python::class_<Hello>("Hello")
    // This method takes a string as parameter and print it
    .def("printChar", &Hello::printChar)
    // This method takes a vector<unsigned char> parameter
    .def("storeFile", &Hello_storeFile)
    ;
}

自定义转换器

转换器注册有三个部分:

  • 检查PyObject是否可转换的函数。返回NULL表示PyObject无法使用已注册的转换器。
  • 构造函数,用于从PyObject构造C ++类型。仅当converter(PyObject)未返回NULL时才会调用此函数。
  • 将构建的C ++类型。

因此,对于给定的C ++类型,如果converter(PyObject)返回非NULL值,则construct(PyObject)将创建C ++类型。 C ++类型作为注册表的一个键,因此Boost.Python不应执行非预期的转换。

在问题的上下文中,我们想要std::vector<unsigned char>的转换器,converter(PyObject)如果NULLPyObjectPyString返回非converter(PyObject)PyObject 1}}将使用std::vector<unsigned char>来创建和填充std::vector<unsigned char>。仅当导出的C ++函数具有std::string(或const引用)参数且python提供的参数是字符串时,才会发生此转换。因此,此自定义转换器不会影响具有#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 that allows for conversions of python strings to // vectors. struct pystring_converter { /// @note Registers converter from a python interable type to the /// 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 is a string. static void* convertible(PyObject* object) { return PyString_Check(object) ? object : NULL; } /// @brief Convert PyString to Container. /// /// Container Concept requirements: /// /// * Container::value_type is CopyConstructable from char. /// * Container can be constructed and populated with two 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 is a borrowed reference, so create a handle indicting it is // borrowed for proper reference counting. python::handle<> handle(python::borrowed(object)); // Obtain a handle to the memory block that the converter has allocated // for the C++ type. typedef python::converter::rvalue_from_python_storage<Container> storage_type; void* storage = reinterpret_cast<storage_type*>(data)->storage.bytes; // Allocate the C++ type into the converter's memory block, and assign // its handle to the converter's convertible variable. The C++ // container is populated by passing the begin and end iterators of // the python object to the 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") // This method takes a string as parameter and print it .def("printChar", &Hello::printChar) // This method takes a vector<unsigned char> parameter .def("storeFile", &Hello::storeFile) ; } 参数的导出函数。

这是一个完整的例子。我选择使转换器通用,以允许从python字符串构造多个类型。凭借其链接支持,它应该具有与其他Boost.Python类型相同的感觉。

>>> from hello import Hello
>>> h = Hello()
>>> h.printChar('abc')
printChar: abc
>>> h.storeFile('def')
storeFile: 3: def
>>> h.storeFile([c for 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> >)

示例用法:

{{1}}

有关自定义转换器和C ++容器的更多信息,请考虑阅读this answer。