Exponiendo las funciones de C ++, que devuelven el puntero usando Boost.Python

Quiero exponer la siguiente función de C ++ a Python usando Boost.Python:

int* test1() { return new int(42); } // Now exposing the function with Boost.Python BOOST_PYTHON_MODULE(libtest1) { using namespace boost::python; def("test1", test1); } 

Cuando bash comstackr esta biblioteca, el error se produce debido a (es mi suposición) Boost.Python no sabe cómo convertir int * a PyObject.

Creo que lo que hay que hacer es definir la estructura de conversión, algo como esto:

 template struct int_ptr_to_python { static PyObject* convert(int* i_ptr) { return i_ptr; } }; 

Y pásalo a la statement BOOST_PYTHON_MODULE:

 BOOST_PYTHON_MODULE(libtest1) { using namespace boost::python; def("test1", test1); to_python_converter<int*, int_ptr_to_python >(); } 

Pero tampoco funciona. Y no puedo encontrar ninguna información sobre cómo deben manejarse las funciones, los punteros de retorno.

¿Alguien puede ayudar?

En resumen, no se puede exponer directamente una función que devuelva int* con Boost.Python, ya que no hay un tipo correspondiente significativo en Python dado que los enteros son inmutables.

  • Si el objective es devolver un número a Python, entonces devuelva int por valor. Esto puede requerir el uso de una función auxiliar para adaptar las API heredadas.
  • Si el objective es devolver un puntero a un objeto asignado con una nueva expresión, entonces el objeto debe ser una clase o unión, y la función debe estar expuesta con políticas específicas para indicar las responsabilidades de propiedad.

En lugar de presentar la solución final de inmediato, me gustaría tomarme el tiempo para repasar los compiler errors. Con Boost.Python, a veces se usan aserciones estáticas pre-C ++ 11 para proporcionar instrucciones en los mensajes del comstackdor. Desafortunadamente, puede ser un poco difícil encontrarlos entre las plantillas pesadas.

El siguiente código:

 #include  int* make_int() { return new int(42); } BOOST_PYTHON_MODULE(example) { namespace python = boost::python; python::def("make_int", &make_int); } 

produce la siguiente salida relevante en clang, con los detalles importantes acentuados en negrita:

 ... / boost / python / detail / caller.hpp: 102: 98: error:
   ningún miembro llamado 'get_pytype' en 'boost :: python :: detail ::
     especificar_a_return_value_policy_to_wrap_functions_returning  '
   ... create_result_converter ((PyObject *) 0, (ResultConverter *) 0, 
                              (ResultConverter *) 0) .g ...

Boost.Python nos informa que se debe especificar un boost::python::return_value_policy para las funciones que devuelven int* . Hay varios modelos de ResultConverterGenerators . Muchas veces las políticas se utilizan para controlar la semántica de propiedad o de vida útil del objeto devuelto. Como la función en el código original está devolviendo un nuevo puntero directamente a Python, boost::python::manage_new_object es apropiado si se espera que la persona que llama asum la responsabilidad de eliminar el objeto.

La especificación de una política para la gestión de objetos todavía falla:

 #include  int* make_int() { return new int(42); } BOOST_PYTHON_MODULE(example) { namespace python = boost::python; python::def("make_int", &make_int, python::return_value_policy()); } 

produce la siguiente salida relevante:

 ... / boost / python / object / make_instance.hpp: 27: 9:
   error: no hay función coincidente para la llamada a 'assertion_failed'
     BOOST_MPL_ASSERT ((mpl :: or_ , is_union >)) ;

En este caso, Boost.Python nos informa que el objeto devuelto desde una función con managed_new_object ResultConverterGenerator debe ser una class o union . Para un int* , la solución más adecuada es devolver el int por valor al pasar a través de la capa Boost.Python. Sin embargo, para completar, a continuación se demuestra:

  • Usando una función auxiliar para adaptar una API heredada.
  • Cómo limitar la creación de un tipo definido por el usuario con una función de fábrica que devuelve punteros.
 #include  /// Legacy API. int* make_int() { return new int(42); } /// Auxiliary function that adapts the legacy API to Python. int py_make_int() { std::auto_ptr ptr(make_int()); return *ptr; } /// Auxiliary class that adapts the legacy API to Python. class holder : private boost::noncopyable { public: holder() : value_(make_int()) {} int get_value() const { return *value_; } void set_value(int value) { *value_ = value; } private: std::auto_ptr value_; }; /// Factory function for the holder class. holder* make_holder() { return new holder(); } BOOST_PYTHON_MODULE(example) { namespace python = boost::python; python::def("make_int", &py_make_int); python::class_("Holder", python::no_init) .add_property("value", python::make_function(&holder::get_value), python::make_function(&holder::set_value)) ; python::def("make_holder", &make_holder, python::return_value_policy()); } 

Uso interactivo:

 >>> import example >>> assert(42 == example.make_int()) >>> holder = example.Holder() Traceback (most recent call last): File "", line 1, in  RuntimeError: This class cannot be instantiated from Python >>> holder = example.make_holder() >>> assert(42 == holder.value) >>> holder.value *= 2 >>> assert(84 == holder.value)