2009-10-15 11 views
10

Używam Boost.Python do tworzenia modułów Python z klas C++. I natknąłem się na problem z referencjami.Boost.Python - jak wrócić przez referencję?

Weź pod uwagę następujący przypadek, w którym mam klasę Foo z przeciążonymi metodami uzyskiwania, które mogą zwracać wartość lub odwoływać się.

Określenie, że zwrot po wartości powinien być użyty, było łatwe po tym, jak wpisałem podpis. Ale myślę, że powinno być możliwe zwrócenie referencji również za pomocą return_value_policy. Jednak użycie tego, co wydawało się odpowiednie (doc); return_value_policy<reference_existing_object> nie działa.

Czy źle zrozumiałem, co robi?

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

Uwaga: Wiem, że to może być niebezpieczne odwołać istniejącego obiektu bez zarządzających czas życia.

Aktualizacja:
Wygląda na to, że pracuje dla obiektów, ale nie podstawowe typy danych.
Weź tę poprawioną przykład:

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

które w teście dało oczekiwany rezultat:

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

Odpowiedz

7

W Pythonie istnieje pojęcie niezmiennych typów. Typ niezmienny nie może zmienić swojej wartości. Przykładami wbudowanych typów niezmiennych są int, float i str.

Powiedziawszy to, nie możesz robić, co chcesz, z boost::python, ponieważ sam Python nie pozwala na zmianę wartości zmiennoprzecinkowej zwracanej przez funkcję.

Twój drugi przykład pokazuje jedno rozwiązanie, inny byłoby stworzenie cienkich owijarki i wystawiając że:

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

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

Który jest lepszym rozwiązaniem niż konieczności zmiany oryginalnego C++ klasa.

0

nie wiem zbyt wiele o Boost.Python, więc może źle zrozumieć pytanie, w którym przypadku jest to całkowicie nieprzydatne. Ale tutaj idzie:

W Pythonie nie można wybrać między zwracaniem przez odniesienie lub wartość, rozróżnienie nie ma sensu w Pythonie. Uważam, że najłatwiej jest myśleć o tym, jak wszystko jest obsługiwane przez odniesienie.

Po prostu masz obiekty i masz nazwy dla tych obiektów. Więc

foo = "ryiuy" 

Tworzy ciąg obiektu „ryiuy”, a następnie pozwala odnieść się do tego obiektu String o nazwie „foo”. Więc w Pythonie, kiedy coś przekazujesz, dostajesz ten przedmiot. Nie ma żadnych "wartości" jako takich, więc nie można przekazać wartości. Ale znowu, jest to również ważny punkt widzenia, że ​​nie ma również odniesień, tylko obiekty i ich nazwy.

Tak więc odpowiedź brzmi, jak sądzę, gdy otrzymasz referencję w C, musisz przekazać odwołanie do obiektu, który odwołuje się do Pythona. A kiedy otrzymasz wartość w C, musisz przekazać odwołanie do obiektu utworzonego z tej wartości do Pythona.

+0

Tak, wiem, że wszystko to odniesienia w Pythonie. Kiedy tworzę metodę Pythona z Boost.Python, który zwraca "według wartości", zwraca kopię tego typu. Chcę tylko * nie * skopiować, ale zamiast tego utworzyć odniesienie do tej samej instancji. – mandrake

0

Czy jesteś pewien, że obiekt C++ jest kopiowany? Otrzymasz nowy obiekt Pythona za każdym razem, ale który odwołuje się do tego samego obiektu C++. W jaki sposób określasz, że obiekt został skopiowany?

+0

Nie kompiluje się, gdy próbuję użyć referencji. Informacje na temat tego, jak ustalić, czy są to odniesienia czy kopie, są łatwe. Utwórz dwa odniesienia do tego, co Twoim zdaniem jest tym samym obiektem, zmodyfikuj je i zobacz, czy zmiany pojawiają się w drugim. (używanie id (obj) nie działa). – mandrake

+0

Tak, prosiłem o sprawdzenie, czy nie używasz id(). Co to jest błąd kompilacji? – Ben

+0

To jest impuls :: STATIC_ASSERTION_FAILURE, który oznacza, że ​​coś, co robię, jest albo nieobsługiwane, albo nielegalne. Po prostu nie wiem co. – mandrake

2

Myślę, że zamiast tego chcesz return internal reference. Użyłem go wcześniej, aby zrobić coś podobnego.

Edit: Latest doc

+0

To ta sama sprawa. Działa na obiektach, ale nie na podstawowych typach danych. – mandrake

+0

link do dokumentacji dotyczy wersji 1.38, która jest bardzo stara od tego komentarza (1.61 jest aktualna) – ofloveandhate

Powiązane problemy