Convertidor personalizado Boost.Python

Tengo una clase que toma un vector como parámetro (un contenido de archivo binario).

Me gustaría convertir el tipo ‘str’ de python en un vector de carácter sin signo pero solo para uno de mis métodos de clase.

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

El uso del convertidor personalizado parece ser lo que necesito, pero si modifico mi boost :: python :: converter :: registry, se modificará para todas mis llamadas a printChar y todos los métodos de python que pasan una cadena como parámetro se convertirán en vectores.

¿Cómo puedo registrar un convertidor por método?

Hay dos enfoques para este problema:

  • Exportar una función auxiliar como Hello.storeFile que acepta boost::python::str , construye std::vector de la cadena, y delega a la función miembro C ++ Hello::storeFile .
  • Escribe un convertidor personalizado. Si bien los convertidores no se pueden registrar por función, están bastante bien orientados para no realizar conversiones involuntarias. Este enfoque a menudo proporciona más reutilización.

Función de ayuda

El uso de una función auxiliar no afectará a ninguna otra función exportada. Por lo tanto, la conversión entre una cadena de python y std::vector solo ocurrirá para 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(begin, begin + len(str))); } ... BOOST_PYTHON_MODULE(hello) { namespace python = boost::python; python::class_("Hello") // This method takes a string as parameter and print it .def("printChar", &Hello::printChar) // This method takes a vector parameter .def("storeFile", &Hello_storeFile) ; } 

Convertidor personalizado

Un registro de convertidor tiene tres partes:

  • Una función que comprueba si un PyObject es convertible. Un retorno de NULL indica que PyObject no puede usar el convertidor registrado.
  • Una función de construcción que construye el tipo C ++ desde un PyObject . Esta función solo se llamará si el converter(PyObject) no devuelve NULL .
  • El tipo de C ++ que se construirá.

Por lo tanto, para un tipo de C ++ dado, si el converter(PyObject) devuelve un valor no NULL , la construct(PyObject) creará el tipo de C ++. El tipo C ++ sirve como una clave en el registro, por lo que Boost.Python no debe realizar conversiones involuntarias.

En el contexto de la pregunta, queremos un convertidor para std::vector donde converter(PyObject) devuelve PyObject NULL si PyObject es un PyString , y converter(PyObject) usará PyObject para crear y completar std::vector . Esta conversión solo ocurrirá si para las funciones C ++ exportadas que tienen un parámetro std::vector (o una referencia de const) y el argumento proporcionado por python es una cadena. Por lo tanto, este convertidor personalizado no afectará las funciones exportadas que tienen parámetros std::string .

Aquí hay un ejemplo completo. He optado por hacer el convertidor genérico para permitir que se puedan construir múltiples tipos desde una cadena python. Con su soporte de encadenamiento, debería tener la misma sensación que otros tipos Boost.Python.

 #include  #include  #include  #include  #include  #include  class Hello { public: void printChar(const std::string& str) { std::cout << "printChar: " << str << std::endl; } void storeFile(const std::vector& 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  pystring_converter& from_python() { boost::python::converter::registry::push_back( &pystring_converter::convertible, &pystring_converter::construct, boost::python::type_id()); 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. /// Ie Container(begin, end) template  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 storage_type; void* storage = reinterpret_cast(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 >() .from_python >() ; python::class_("Hello") // This method takes a string as parameter and print it .def("printChar", &Hello::printChar) // This method takes a vector parameter .def("storeFile", &Hello::storeFile) ; } 

Y el ejemplo de uso:

 >>> 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 "", line 1, in  Boost.Python.ArgumentError: Python argument types in Hello.storeFile(Hello, list) did not match C++ signature: storeFile(Hello {lvalue}, std::vector >) 

Para obtener más información sobre los convertidores personalizados y los contenedores C ++, considere leer esta respuesta.