2009-10-15 11 views
10

Sto usando Boost.Python per creare moduli Python dalle classi C++. E ho avuto un problema con i riferimenti.Boost.Python - Come restituire per riferimento?

Condider il seguente caso in cui ho una classe Foo con metodi get sovraccaricati che possono essere restituiti per valore o riferimento.

Specificare che il ritorno in valore deve essere utilizzato è stato facile dopo aver digitato una firma. Ma penso che dovrebbe essere possibile anche restituire un riferimento usando uno return_value_policy. Tuttavia, utilizzando ciò che sembrava appropriato (doc); return_value_policy<reference_existing_object> non sembra funzionare.

Ho frainteso cosa fa?

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: so che potrebbe essere pericoloso fare riferimento a un oggetto esistente senza la gestione della vita.

Aggiornamento:
Sembra che funziona per gli oggetti, ma non i tipi di dati di base.
Prendete questo esempio rivisto:

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); 
     ; 
} 

Che a un test ha dato il risultato atteso:

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

risposta

7

In Python, c'è il concetto di tipi immutabili. Un tipo immutabile non può avere il suo valore modificato. Esempi di tipi immutabili incorporati sono int, float e str.

Detto questo, non è possibile fare ciò che si desidera con boost::python, perché Python stesso non consente di modificare il valore del float restituito dalla funzione.

Il secondo esempio mostra una soluzione, un altro sarebbe quello di creare thin-involucri ed esponendo che:

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

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

che è una soluzione migliore di dover cambiare l'originale C++ classe.

0

non so molto su Boost.Python, così che io possa fraintendere la questione, in cui caso questo è del tutto inutile. Ma ecco:

In Python non è possibile scegliere tra il ritorno per riferimento o per valore, la distinzione non ha senso in Python. Trovo che sia più facile pensarlo come tutto ciò che viene gestito per riferimento.

Hai solo oggetti e hai nomi per questi oggetti. Così

foo = "ryiuy" 

Crea la stringa oggetto "ryiuy" e quindi consente di fare riferimento a tale oggetto stringa con il nome "foo". Quindi in Python, quando si passa qualcosa, si ottiene quell'oggetto. Non ci sono "valori" in quanto tali, quindi non è possibile passare il valore. Ma poi di nuovo, è anche un punto di vista valido che non ci sono riferimenti, solo oggetti e il loro nome.

Quindi la risposta è, immagino, è che quando si ottiene un riferimento in C, è necessario passare un riferimento all'oggetto che fa riferimento a riferimenti in Python. E quando ottieni un valore in C, devi passare un riferimento all'oggetto che crei da quel valore in Python.

+0

Sì, so che tutto è riferimento in Python. Quando creo un metodo python con Boost.Python che restituisce "per valore" restituisce una copia di quel tipo. Quello che voglio fare è * non * copiare, ma creare invece un riferimento alla stessa istanza. – mandrake

0

Sei sicuro che l'oggetto C++ sia stato copiato? Otterrete un nuovo oggetto python ogni volta ma che fa riferimento allo stesso oggetto C++. Come stai determinando che l'oggetto è stato copiato?

+0

Non viene compilato quando provo a utilizzare i riferimenti. Come determinare se sono riferimenti o copie è facile. Crea due riferimenti a ciò che pensi sia lo stesso oggetto, modificane uno e verifica se le modifiche appaiono nell'altro. (usando id (obj) non funzionerebbe). – mandrake

+0

Sì, chiedevo di verificare che non stavi usando id(). Qual'è l'errore di compilazione? – Ben

+0

È un boost :: STATIC_ASSERTION_FAILURE, che indica che qualcosa che faccio non è supportata o illegale. Io proprio non so cosa. – mandrake

2

Penso che tu voglia return internal reference invece. L'ho usato prima per fare qualcosa di simile.

Edit: Latest doc

+0

È lo stesso affare. Funziona su oggetti ma non su tipi di dati di base. – mandrake

+0

il collegamento alla documentazione è per la versione 1.38, che è molto vecchia a partire da questo commento (1.61 è attuale) – ofloveandhate

Problemi correlati