2009-10-15 11 views
10

Estoy usando Boost.Python para crear módulos de Python a partir de clases de C++. Y encontré un problema con las referencias.Boost.Python - ¿Cómo devolver por referencia?

Considere el siguiente caso en el que tengo una clase Foo con métodos de obtención sobrecargados que pueden devolver por valor o por referencia.

Especificar que el retorno por valor debería ser utilizado fue fácil una vez que escribí una firma. Pero creo que debería ser posible devolver una referencia también usando un return_value_policy. Sin embargo, usando lo que parecía apropiado (doc); return_value_policy<reference_existing_object> no pareció funcionar.

¿He entendido mal qué hace?

struct Foo { 
    Foo(float x) { _x = x; } 
    float& get() { return _x; } 
    float get() const { return _x; } 
private: 
    float _x; 
}; 

// Wrapper code 
BOOST_PYTHON_MODULE(my_module) 
{ 
    using namespace boost::python; 
    typedef float (Foo::*get_by_value)() const; 
    typedef float& (Foo::*get_by_ref)(); 

    class_<Foo>("Foo", init<float>()) 
     .def("get", get_by_value(&Foo::get)) 
     .def("get_ref", get_by_ref(&Foo::get), 
      return_value_policy<reference_existing_object>())//Doesn't work 
     ; 
} 

Nota: Sé que podría ser peligroso hacer referencia al objeto existente sin la administración de por vida.

Actualización:
Parece que funciona para los objetos, pero no tipos de datos básicos.
Tome este ejemplo revisada:

struct Foo { 
    Foo(float x) { _x = x; } 
    float& get() { return _x; } 
    float get() const { return _x; } 
    void set(float f){ _x = f;} 
    Foo& self(){return *this;} 
private: 
    float _x; 
}; 

// Wrapper code 
using namespace boost::python; 
BOOST_PYTHON_MODULE(my_module) 
{ 
    typedef float (Foo::*get_by_value)() const; 

    class_<Foo>("Foo", init<float>()) 
     .def("get", get_by_value(&Foo::get)) 
     .def("get_self", &Foo::self, 
      return_value_policy<reference_existing_object>()) 
     .def("set", &Foo::set); 
     ; 
} 

Lo que en una prueba dio el resultado esperado:

>>> foo1 = Foo(123) 
>>> foo1.get() 
123.0 
>>> foo2 = foo1.get_self() 
>>> foo2.set(1) 
>>> foo1.get() 
1.0 
>>> id(foo1) == id(foo2) 
False 

Respuesta

7

En Python, está el concepto de tipos inmutables . Un tipo inmutable no puede cambiar su valor. Ejemplos de tipos inmutables incorporados son int, float y str.

Habiendo dicho eso, no puede hacer lo que quiera con boost::python, porque Python no le permite cambiar el valor del flotador devuelto por la función.

Su segundo ejemplo muestra una solución, otra sería crear delgadas envolturas y exponiendo que:

void Foo_set_x(Foo& self, float value) { 
    self.get() = value; 
} 

class_<Foo>("Foo", init<float>()) 
    ... 
    .def("set", &Foo_set_x); 
; 

que es una solución mejor que tener que cambiar el original de C++ clase.

0

no sé mucho acerca de Boost.Python, por lo que puede malinterpretar la pregunta, en el cual caso esto es completamente inútil. Pero aquí va:

En Python no puede elegir entre devolver por referencia o por valor, la distinción no tiene sentido en Python. Encuentro que es más fácil pensar que todo se maneja por referencia.

Solo tiene objetos y tiene nombres para esos objetos. Así

foo = "ryiuy" 

Crea la cadena de objeto "ryiuy" y luego permite hacer referencia a ese objeto cadena con el nombre "nombre". Entonces, en Python, cuando pasas algo, pasas ese objeto. No hay "valores" como tales, por lo que no puede pasar el valor. Pero, de nuevo, también es un punto de vista válido que tampoco hay referencias, solo objetos y sus nombres.

Así que la respuesta es, supongo, que cuando obtiene una referencia en C, debe pasar una referencia al objeto que las referencias hacen referencia a Python. Y cuando obtiene un valor en C, necesita pasar una referencia al objeto que crea a partir de ese valor en Python.

+0

Sí, sé que todo es referencias en Python. Cuando creo un método de Python con Boost.Python que devuelve "por valor", devuelve una copia de ese tipo. Lo que quiero hacer es * no * copiar, sino crear una referencia a la misma instancia. – mandrake

0

¿Estás seguro de que el objeto C++ se está copiando? Obtendrá un nuevo objeto python cada vez pero que haga referencia al mismo objeto C++. ¿Cómo estás determinando que el objeto ha sido copiado?

+0

No compila cuando trato de usar referencias. En cuanto a cómo determinar si es referencias o copias es fácil. Cree dos referencias a lo que cree que es el mismo objeto, modifique uno y vea si los cambios aparecen en el otro. (usar id (obj) no funcionaría). – mandrake

+0

Sí, preguntaba para verificar que no estaba usando id(). ¿Cuál es el error de compilación? – Ben

+0

Es un impulso :: STATIC_ASSERTION_FAILURE, que indica que algo que hago no es compatible o es ilegal. Simplemente no sé qué. – mandrake

2

Creo que quieres return internal reference en su lugar. Lo he usado antes para hacer algo similar.

Editar: Latest doc

+0

Es el mismo trato. Funciona en objetos pero no en tipos de datos básicos. – mandrake

+0

el enlace a la documentación es para la versión 1.38, que es muy antiguo a partir de este comentario (1.61 es actual) – ofloveandhate

Cuestiones relacionadas