簡體   English   中英

Boost.Python自定義轉換器

[英]Boost.Python custom converter

我有一個類作為參數(二進制文件內容)。

我想將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的所有調用以及所有傳遞string的python方法將參數轉換為vector。

如何注冊每個方法的轉換器?

這個問題有兩種方法:

  • 將helper函數導出為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> ,其中如果PyObjectPyStringconverter(PyObject) converter(PyObject)返回非NULLconverter(PyObject)將使用PyObject創建和填充std::vector<unsigned char> 僅當導出的C ++函數具有std::vector<unsigned char> (或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 ++容器的更多信息,請考慮閱讀答案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM