【问题标题】:Boost.Python custom converterBoost.Python 自定义转换器
【发布时间】:2013-04-12 21:37:37
【问题描述】:

我有一个类以向量作为参数(二进制文件内容)。

我想将 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) }

使用自定义转换器似乎是我所需要的,但如果我修改我的 boost::python::converter::registry 它将修改我对 printChar 的所有调用,并且所有将字符串作为参数传递的 python 方法都将转换为向量.

如何注册每个方法转换器?

【问题讨论】:

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


    【解决方案1】:

    解决这个问题有两种方法:

    • 将一个辅助函数导出为Hello.storeFile,它接受boost::python::str,从字符串构造std::vector&lt;unsigned char&gt;,并委托给C++ Hello::storeFile 成员函数。
    • 编写自定义转换器。虽然转换器不能在每个功能的基础上注册,但它们的范围相当好,不会执行任何意外的转换。这种方法通常提供更高的可重用性。

    辅助函数

    使用辅助函数不会影响任何其他导出的函数。因此,python 字符串和std::vector&lt;unsigned char&gt; 之间的转换只会发生在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&lt;unsigned char&gt; 的转换器,其中 converter(PyObject) 返回非 NULL 如果 PyObjectPyStringconverter(PyObject) 将使用 PyObject 来创建和填充std::vector&lt;unsigned char&gt;。仅当导出的 C++ 函数具有 std::vector&lt;unsigned char&gt;(或 const 引用)参数并且从 python 提供的参数是字符串时,才会发生这种转换。因此,此自定义转换器不会影响具有std::string 参数的导出函数。

    这是一个完整的例子。我选择使转换器通用,以允许从 python 字符串构造多种类型。由于它的链式支持,它应该与其他 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)
        ;
    }
    

    以及示例用法:

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

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

    【讨论】:

      猜你喜欢
      • 2014-03-17
      • 2012-03-26
      • 1970-01-01
      • 2016-04-19
      • 2012-03-15
      • 2021-01-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多