2010-07-27 14 views
13

Tengo una biblioteca que crea objetos (instancias de clase A) y los pasa a un programa python que debería poder llamar a sus métodos.pasar instancias de clases C++ a python con boost :: python

Básicamente tengo instancias de clase C++ y quiero usarlas de python. Ocasionalmente, ese objeto debería pasarse a C++ para algunas manipulaciones.

creé el siguiente archivo de envoltura (vamos a suponer que la función New se llama en alguna parte del código en C++):

#include <boost/python.hpp> 
#include <iostream> 
#include <boost/smart_ptr.hpp> 

using namespace boost; 
using namespace boost::python; 

int calls = 0; 

struct A 
{ 
    int f() { return calls++; } 
    ~A() { std::cout << "destroyed\n"; } 
}; 

shared_ptr<A> existing_instance; 

void New() { existing_instance = shared_ptr<A>(new A()); } 

int Count(shared_ptr<A> a) { return a.use_count(); } 

BOOST_PYTHON_MODULE(libp) 
{ 
    class_<A>("A") 
     .def("f", &A::f) 
    ; 

    def("Count", &Count); 

    register_ptr_to_python< shared_ptr<A> >(); 
} 

El código carece de la parte en la que la pitón obtiene el existing_instance. No pegué eso, pero digamos que uso un mecanismo de devolución de llamada para ese propósito.

Este código funciona, pero tengo algunas preguntas:

  1. En la función Count (y en todas las demás funciones de manipulación de C++) es que estaba bien para pasar a así o es mejor hacer algo como const shared_ptr<A>& ? En los fragmentos de código que encontré en la documentación de boost de python, la referencia se usa a menudo, pero no entiendo la diferencia (aparte de tener un contador de referencia más alto, por supuesto).

  2. ¿Este código es "seguro"? Cuando paso la instancia_existente a python, su contador se incrementará (solo una vez, incluso si en python hago más copias del objeto, por supuesto), así que no hay forma de que el código C++ pueda destruir el objeto en la medida en que python al menos una "copia". ¿Estoy en lo correcto? Traté de jugar con los punteros y parece que estoy en lo correcto, solo estoy preguntando para estar seguro.

  3. Me gustaría evitar que python cree instancias de A. Solo deberían pasarse desde código C++. ¿Cómo podría lograr eso? EDITAR: encontrado, sólo tiene que utilizar NO_INIT y noncopyable: class_<A, boost::noncopyable>("A", no_init)

Respuesta

14

boost::python sabe todo sobre boost::shared_ptr, pero hay que decirle que boost::shared_ptr<A> contiene una instancia de A, esto se hace mediante la adición de boost::shared_ptr<A> en la lista de argumentos de plantilla para class_, más información sobre este 'Tipo Held es here in the boost documentation.

Para evitar casos se crean a partir de pitón, se agrega boost::python::no_init al constructor class_, por lo que terminan con:

boost::python::class_< A, boost::shared_ptr<A> >("A", boost::python::no_init) 
    //... .def, etc 
    ; 

En generales usted no debe pasar punteros alrededor compartidos por referencia, ya que si el la referencia al puntero compartido se invalida, entonces la referencia a la que apunta el puntero compartido también se invalida (ya que tomar una referencia del puntero compartido no incrementa el contador de referencia al objeto apuntado).

Es perfectamente seguro pasar objetos boost::shared_ptr hacia y desde python, los recuentos de referencia (python y shared_ptr) se administrarán correctamente siempre que no cambie el return_value_policy. Si cambia la política de un método expuesto en python para que devuelva una referencia a un puntero compartido, entonces puede causar problemas, del mismo modo que pasar punteros compartidos por referencias de C++ puede causar problemas.

(. Además, se debe utilizar make_shared<A>(...) con preferencia a shared_ptr<A>(new A(...)))

+0

cuál es la diferencia (en la práctica) entre "class_ (" A "NO_INIT)" y "impulso: : python :: class_ > ("A", boost :: python :: no_init) "? El código que publiqué funciona perfectamente incluso sin especificar "boost :: shared_ptr " después de "class_". Entonces, ¿por qué iba a necesitar eso? – Emiliano

+0

Especifica el tipo predeterminado que será empaquetado por una python 'A' - así que si tiene una función C++ que devuelve una 'A', 'A *' shared_ptr o algo similar, y lo expone en python, entonces la el valor de retorno se mantendrá como shared_ptr , que es lo que desea si con frecuencia está pasando estos objetos dentro y fuera de python, ya que toda la propiedad se manejará correctamente. Consulte http://www.boost.org/doc/libs/1_43_0/libs/python/doc/v2/class.html#HeldType (especialmente el punto 2) para obtener más información. – James

1

En esta situación mi código sería así (por el ejemplo):

... 

BOOST_PYTHON_MODULE(libp) 
{ 
    class_<A, boost::shared_ptr<A>, boost::noncopyable >("A") 
     .def("f", &A::f) 
     .def("Count", &Count) 
    ; 
} 

Es importante prohibir boost :: python para copiar cosas, pero si está usando shared_ptr es probable que solo necesite copiar en algunas situaciones controladas.

Cuestiones relacionadas