简体   繁体   English

Boost.Python自定义转换器

[英]Boost.Python custom converter

I have a class taking a vector as parameter (a binary file content). 我有一个类作为参数(二进制文件内容)。

I would like to convert python 'str' type into vector of unsigned char but only for one of my class method. 我想将python'str'类型转换为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) }

Using custom converter seems to be what I need but if I modify my boost::python::converter::registry it will be modify for all my calls to printChar and all python methods passing string as parameter will be converted to vector. 使用自定义转换器似乎是我需要的但是如果我修改我的boost :: python :: converter :: registry它将修改我对printChar的所有调用以及所有传递string的python方法将参数转换为vector。

How can I register a per-method converter ? 如何注册每个方法的转换器?

There are two approaches to this problem: 这个问题有两种方法:

  • Export a helper function as Hello.storeFile that accepts boost::python::str , constructs std::vector<unsigned char> from the string, and delegates to the C++ Hello::storeFile member function. 将helper函数导出为Hello.storeFile ,接受boost::python::str ,从字符串构造std::vector<unsigned char> ,并委托给C ++ Hello::storeFile成员函数。
  • Write a custom converter. 编写自定义转换器。 While converters cannot be registered on a per-function basis, they are fairly well scoped as to not perform any unintended conversions. 虽然转换器无法基于每个功能进行注册,但它们的范围非常合理,因为它们不会执行任何意外的转换。 This approach often provides more reusability. 这种方法通常提供更多的可重用性。

Helper Function 辅助功能

Using a helper function will not affect any other exported function. 使用辅助函数不会影响任何其他导出函数。 Thus, the conversion between a python string and std::vector<unsigned char> will only occur for 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)
    ;
}

Custom Converter 定制转换器

A converter registration has three parts: 转换器注册有三个部分:

  • A function that checks if a PyObject is convertible. 检查PyObject是否可转换的函数。 A return of NULL indicates that the PyObject cannot use the registered converter. 返回NULL表示PyObject无法使用已注册的转换器。
  • A construct function that constructs the C++ type from a PyObject . 构造函数,用于从PyObject构造C ++类型。 This function will only be called if converter(PyObject) does not return NULL . 仅当converter(PyObject)不返回NULL时才会调用此函数。
  • The C++ type that will be constructed. 将构造的C ++类型。

Therefore, for a given C++ type, if converter(PyObject) returns a non- NULL value, then construct(PyObject) will create the C++ type. 因此,对于给定的C ++类型,如果converter(PyObject)返回非NULL值,则construct(PyObject)将创建C ++类型。 The C++ type serves as a key into the registry, so Boost.Python should not perform unintended conversions. C ++类型充当注册表的键,因此Boost.Python不应执行非预期的转换。

In context of the question, we want a converter for std::vector<unsigned char> where converter(PyObject) returns non- NULL if PyObject is a PyString , and converter(PyObject) will use PyObject to create and populate std::vector<unsigned char> . 在这个问题的上下文中,我们想要一个转换器用于std::vector<unsigned char> ,其中如果PyObjectPyStringconverter(PyObject) converter(PyObject)返回非NULLconverter(PyObject)将使用PyObject创建和填充std::vector<unsigned char> This conversion will only occur if for exported C++ functions that have a std::vector<unsigned char> (or a const reference) parameter and the argument provided from python is a string. 仅当导出的C ++函数具有std::vector<unsigned char> (或const引用)参数且python提供的参数是字符串时,才会发生此转换。 Therefore, this custom converter will not affect exported functions that have std::string parameters. 因此,此自定义转换器不会影响具有std::string参数的导出函数。

Here is a complete example. 这是一个完整的例子。 I have opted to make the converter generic to allow multiple types to be constructable from a python string. 我选择使转换器通用,以允许从python字符串构造多个类型。 With its chaining support, it should have the same feel as other Boost.Python types. 凭借其链接支持,它应该具有与其他Boost.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)
    ;
}

And the example usage: 以及示例用法:

>>> 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> >)

For more on custom converters and C++ containers, consider reading this answer. 有关自定义转换器和C ++容器的更多信息,请考虑阅读答案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM