Narzędzia użytkownika

Narzędzia witryny


boost_python

Różnice

Różnice między wybraną wersją a wersją aktualną.

Odnośnik do tego porównania

Both sides previous revision Previous revision
Next revision
Previous revision
boost_python [2008/04/14 19:56]
kamituel
boost_python [2008/04/16 07:24] (aktualna)
kamituel
Linia 1: Linia 1:
 ====== Biblioteka Boost Python ====== ====== Biblioteka Boost Python ======
 Kamil Leszczuk, G1SST Kamil Leszczuk, G1SST
 +
 +==== Wstęp ====
  
 Biblioteka Boost Python umożliwia korzystanie z kodu napisanego w C++ z poziomu skryptów Pythona. Biblioteka Boost Python umożliwia korzystanie z kodu napisanego w C++ z poziomu skryptów Pythona.
    
 Do dyspozycji mamy nie tylko proste funkcje, ale także całe klasy (i ich hierarchie) czy metody klas - wraz z tymi wirtualnymi. Do dyspozycji mamy nie tylko proste funkcje, ale także całe klasy (i ich hierarchie) czy metody klas - wraz z tymi wirtualnymi.
 +Wykorzystać można także przeciążone operatory czy pola tylko do odczytu.
 +\\ Biblioteka Boost Python jest wygodnym narzędziem także ze względu na prostotę swojego interfejsu - zdecydowana większość kodu C++ ma duże szanse współpracować z tą bilblioteką out-of-box, bez niespodzianek i problemów.
 +
 +==== Wymagania ====
 +
 +Aby rozpocząć pracę z bibliotekami boost::​python należy posiadać w systemie (oprócz kompilatora oczywiście):​
 +  * Interpreter Pythona
 +  * Pakiet //devel// Pythona - pliki nagłówkowe (jeśli nie zostały dostarczone wraz z instalacją Pythona)
 +  * Biblioteki boost
 +
  
 ==== Pierwsze lody ==== ==== Pierwsze lody ====
Linia 20: Linia 32:
  
 BOOST_PYTHON_MODULE(p1) { BOOST_PYTHON_MODULE(p1) {
-        def("​skomplikowany_algorytm",​ &​potega);​+        ​.def("​skomplikowany_algorytm",​ &​potega);​
 } }
 </​code>​ </​code>​
-Widzimy jednoparametrową funkcję potega. Nie różni się ona niczym innych funkcji napisanych w C/C++. Ciekawsze są linijki znajdujące się bezpośrednio pod nią - dajemy tam znać, że tworzymy moduł Pythona o nazwie ''​p1'',​ który udostępni jedną funkcję o nazwie ''​skomplikowany_algorytm''​. Zwróćmy uwagę, że nazwa funkcji udostępniona przez moduł Pythona nie musi pokrywać się z nazwą funkcji z C++.\\  +Widzimy jednoparametrową funkcję potega. Nie różni się ona niczym innych funkcji napisanych w C/C++. Ciekawsze są linijki znajdujące się bezpośrednio pod nią - dajemy tam znać, że tworzymy moduł Pythona o nazwie ​**''​p1''​**, który udostępni jedną funkcję o nazwie ​**''​skomplikowany_algorytm''​**. Zwróćmy uwagę, że nazwa funkcji udostępniona przez moduł Pythona nie musi pokrywać się z nazwą funkcji z C++. 
-Czas na kompilację - należy zapewnić kompilatorowi dostęp do odpowiednich nagłówków (bibliotek boost i Pythona), a także linkować z biblioteką boost python, np.:+ 
 + 
 +Czas na kompilację - należy zapewnić kompilatorowi ​**dostęp do odpowiednich nagłówków** (bibliotek boost i Pythona), a także ​**linkować z biblioteką boost python**, np.:
 <code shell> <code shell>
 g++ -Wall -shared -I/​usr/​include/​boost/​ -I/​usr/​include/​python2.5/​ /​usr/​lib/​libboost_python-mt.so p1.cpp -o p1.so g++ -Wall -shared -I/​usr/​include/​boost/​ -I/​usr/​include/​python2.5/​ /​usr/​lib/​libboost_python-mt.so p1.cpp -o p1.so
 </​code>​ </​code>​
-Jako wynik powinniśmy uzyskać plik ''​p1.so'',​ który jest właśnie naszym modułem Pythona. Stwórzmy więc skrypt który z tego modułu skorzysta +Jako wynik powinniśmy uzyskać plik ''​p1.so'',​ który jest właśnie naszym modułem Pythona. Stwórzmy więc skrypt który z tego modułu skorzysta
-\\ **Uwaga:** moduł powinien znaleźć się w tym samym katalogu co skrypt, aby interpreter mógł go bezproblemowo znaleźć ( katalog '​.'​ znajduje się w domyślnej ścieżce poszukiwania Pythona).\\ + 
 + 
 +**Uwaga:** moduł powinien znaleźć się w tym samym katalogu co skrypt, aby interpreter mógł go bezproblemowo znaleźć ( katalog ​bierzący ​'​.'​ znajduje się w domyślnej ścieżce poszukiwania Pythona). 
 + 
 <code python> <code python>
 +#​!/​usr/​bin/​python
 import p1 import p1
  
 print p1.skomplikowany_algorytm(7) print p1.skomplikowany_algorytm(7)
 </​code>​ </​code>​
-Uruchommy ​nasz skrypt (pamiętając aby ewentualnie zmienić ścieżkę do interpretatora Pythona), np.:+Wykonajmy ​nasz skrypt (pamiętając aby ewentualnie zmienić ścieżkę do interpretatora Pythona, jeżeli w systemie jest inna), np.:
 <code shell> <code shell>
 $ ./skrypt.py $ ./skrypt.py
Linia 82: Linia 101:
         virtual void metoda ( std::string suffix ) {         virtual void metoda ( std::string suffix ) {
                 this->​get_override("​metoda"​)();​                 this->​get_override("​metoda"​)();​
 +                // Dla kompilatora MSVC należy zamienić powyższą linijkę na:
 +                // this->​get_override("​metoda"​).ptr();​
         }         }
 }; };
Linia 93: Linia 114:
 } }
 </​code>​ </​code>​
-Potrzebujemy sppecjalnego wrappera dla ''​Costam_0''​ ponieważ twórcy biblioteki uznali, że najlepiej jest gdy pisząc moduł Pythona nie musimy modyfikować istniejącego kodu C++.\\ Wrapper ten nie robi właściwie nic oprócz wołania odpowiedniej metody. Warto zauważyć, że potrzebujemy go właściwie ​tylko dlatego, że klasa ''​Costam_0''​ posiada czysto wirtualną metodę.\\  +Potrzebujemy sppecjalnego wrappera dla ''​Costam_0''​ ponieważ twórcy biblioteki uznali, że najlepiej jest gdy pisząc moduł Pythona nie musimy modyfikować istniejącego kodu C++.\\ Wrapper ten nie robi właściwie nic oprócz wołania odpowiedniej metody. Warto zauważyć, że potrzebujemy go tylko dlatego, że klasa ''​Costam_0''​ posiada czysto wirtualną metodę.\\  
-W definicji modułu widzimy, że najpierw deklarujemy chęć udostępnienia klasy - jako nazwę podajemy jednak nazwę ​modułu! Klasa ta widoczna będzie w Pythonie pod nazwą ''​Costam0''​ i będzie posiadała jedną metodę. Następnie definiujemy ​jedną metodę ''​metoda''​ zaznaczając,​ że jest ona czysto wirtualną składową klasy.+W definicji modułu widzimy, że najpierw deklarujemy chęć udostępnienia klasy - jako nazwę podajemy jednak nazwę ​klasy wrappera! Klasa ta widoczna będzie w Pythonie pod nazwą ''​Costam0''​ i będzie posiadała jedną metodę. Następnie definiujemy ​funkcję ''​metoda''​ zaznaczając,​ że jest ona czysto wirtualną składową klasy.
  
 Zajmijmy się teraz klasą ''​Costam_A''​. Dziedziczy ona po ''​Costam_0''​. Aby ją udostępnić,​ musimy dopisać poniższy kod w ramach bloku BOOST_PYTHON_MODULE:​ Zajmijmy się teraz klasą ''​Costam_A''​. Dziedziczy ona po ''​Costam_0''​. Aby ją udostępnić,​ musimy dopisać poniższy kod w ramach bloku BOOST_PYTHON_MODULE:​
Linia 105: Linia 126:
 </​code>​ </​code>​
 Ten kod właściwie sam siebie tłumaczy. Klasa będzie widoczna pod nazwą ''​CostamA''​. Posiadała będzie **dwa** konstruktory - domyślny bezparametrowy oraz z jednym parameterem typu //string// (druga linia). Konstruktor domyślny jest standardowo udostępniany,​ dlatego jawnie tego nie zażądaliśmy. ​ Ten kod właściwie sam siebie tłumaczy. Klasa będzie widoczna pod nazwą ''​CostamA''​. Posiadała będzie **dwa** konstruktory - domyślny bezparametrowy oraz z jednym parameterem typu //string// (druga linia). Konstruktor domyślny jest standardowo udostępniany,​ dlatego jawnie tego nie zażądaliśmy. ​
-Klasa dziedziczy po ''​Costam_0''​ - mówi o tym fragment ''​bases<​Costam_0>''​. \\ +Klasa dziedziczy po ''​Costam_0''​ - mówi o tym fragment ​**''​bases<​Costam_0>''​**. \\ 
 Dodajemy także składową o nazwie ''​nazwa_prop''​ - mechanizm za nią stojący nie jest właściwie dostępny w C++ - zamiast niego używa się akcesorów. Składowa ta zachowywała się będzie tak jak pole klasy z tą różnicą, że zamiast dokonywać bezpośredniego przypisania do niej wartości, skorzystamy z akcesorów C++ (metody ''​daj_nazwe''​ i ''​ustaw_nazwe''​).\\ ​ Dodajemy także składową o nazwie ''​nazwa_prop''​ - mechanizm za nią stojący nie jest właściwie dostępny w C++ - zamiast niego używa się akcesorów. Składowa ta zachowywała się będzie tak jak pole klasy z tą różnicą, że zamiast dokonywać bezpośredniego przypisania do niej wartości, skorzystamy z akcesorów C++ (metody ''​daj_nazwe''​ i ''​ustaw_nazwe''​).\\ ​
 I w końcu - zwróćmy uwagę na brak definicji metody ''​metoda''​ - jako że nasza klasa dziedziczy po ''​Costam_0'',​ i w tamtej klasie tę metodę udostępniliśmy,​ teraz już nie musimy tego robić.\\ ​ I w końcu - zwróćmy uwagę na brak definicji metody ''​metoda''​ - jako że nasza klasa dziedziczy po ''​Costam_0'',​ i w tamtej klasie tę metodę udostępniliśmy,​ teraz już nie musimy tego robić.\\ ​
  
-Ostatnia z klas udostępniona może być w ten sposób:+Ostatnia z klas udostępniona może być w taki sposób:
 <code cpp> <code cpp>
         class_<​Costam_B,​ bases<​Costam_A>​ >​("​CostamB",​ init<​std::​string>​())         class_<​Costam_B,​ bases<​Costam_A>​ >​("​CostamB",​ init<​std::​string>​())
Linia 120: Linia 141:
 Dodajemy pole ''​x_ro''​ klasy, które będzie przyjmowało wartość zmiennej ''​x_'',​ lecz będzie tylko do odczytu - kod Pythona nie będzie mógł zmienić jego wartości. Dodajemy pole ''​x_ro''​ klasy, które będzie przyjmowało wartość zmiennej ''​x_'',​ lecz będzie tylko do odczytu - kod Pythona nie będzie mógł zmienić jego wartości.
 Z kolei własność ''​x_rw''​ jest tym samym, lecz umożliwia dodatkowo zmianę wartości zmiennej. Z kolei własność ''​x_rw''​ jest tym samym, lecz umożliwia dodatkowo zmianę wartości zmiennej.
-W tym przykładzie te dwie własności wskazują na tę samą zmienną z C++, lecz nie ma to znaczenia.+W tym przykładzie te dwie własności wskazują na tę samą zmienną z C++, lecz równie dobrze mogłyby to być zupełnie różne obiekty - nie ma to znaczenia.
  
 Dodatkowo widzimy jeszcze jedną możliwość biblioteki ''​boost::​python''​ - udostępnianie przeciążonych operatorów C++ - tutaj na przykładzie operatora '​+'​ z parametrem typu //string//. Dodatkowo widzimy jeszcze jedną możliwość biblioteki ''​boost::​python''​ - udostępnianie przeciążonych operatorów C++ - tutaj na przykładzie operatora '​+'​ z parametrem typu //string//.
  
 Klasa ta wprowadza także dodatkową nowość - tym razem nie udostępniamy domyślnego,​ bezparametrowego konstruktora. Jak uzyskaliśmy ten efekt? Klasa ta wprowadza także dodatkową nowość - tym razem nie udostępniamy domyślnego,​ bezparametrowego konstruktora. Jak uzyskaliśmy ten efekt?
-Zwróćmy uwagę na pierwszą linijkę - wskazujemy tam, że domyślnie udostępnionym konstruktorem będzie ten pobierający napis.+Zwróćmy uwagę na pierwszą linijkę - wskazujemy tam, że domyślnie udostępnionym konstruktorem będzie ten pobierający napis (''​init<​std::​string>''​).
  
 Skoro mamy już gotowy drugi moduł, przetestujmy go: Skoro mamy już gotowy drugi moduł, przetestujmy go:
Linia 168: Linia 189:
 # Ale to nie zadziala: x_ro jest tylko do odczytu! # Ale to nie zadziala: x_ro jest tylko do odczytu!
 #b.x_ro = "To sie nie zapisze!" ​ #b.x_ro = "To sie nie zapisze!" ​
 +</​code>​
 +Po uruchomieniu naszym oczom ukaże się:
 +<​code>​$ ./p2.py
 +AAA A aaa
 +BBB B bbb
 +CCC C ccc
 +BBB B bbb ++ PLUS DZIALA
 +Zapisalo sie!
 +Zapisalo sie!
 +</​code>​
 +Widzimy więc, że wszystkie elementy działają poprawnie. Co więcej - po odkomentowaniu niektórych fragmentów - np. próby zapisania do zmiennej tylko do odczytu ''​x_ro''​ interpreter poinformuje nas o błędzie:
 +<​code>​$ ./p2.py
 +AAA A aaa
 +BBB B bbb
 +CCC C ccc
 +BBB B bbb ++ PLUS DZIALA
 +Zapisalo sie!
 +Zapisalo sie!
 +Traceback (most recent call last):
 +  File "​./​p2.py",​ line 39, in <​module>​
 +    b.x_ro = "To sie nie zapisze!" ​
 +AttributeError:​ can't set attribute
 </​code>​ </​code>​
  
Linia 175: Linia 218:
   * Kod który piszemy może być wąskim gardłem aplikacji - warto napisać go w C++ ze względu na wydajność   * Kod który piszemy może być wąskim gardłem aplikacji - warto napisać go w C++ ze względu na wydajność
   * Mamy już kod w C++ - po co przepisywać go od nowa?   * Mamy już kod w C++ - po co przepisywać go od nowa?
 +  * Aplikację napisać w C++ a GUI w, dużo przyjemniejszym, ​ Pythonie
   * Mamy dostęp do biblioteki C++, ale nie mamy do niej źródeł   * Mamy dostęp do biblioteki C++, ale nie mamy do niej źródeł
-W tym ostatnim przypadku jedyne co nam będzie potrzebne to pliki nagłówkowe tej bilioteki i parę minut czasu. +W tym ostatnim przypadku jedyne co nam będzie potrzebne to pliki nagłówkowe tej bilioteki i trochę minut czasu. 
-Weźmy klasy z poprzedniego przykładu, rozdzielmy je na plik nagłówkowy i źródłowy. ​Źródłowy skompilujmy do postaci biblioteki współdzielonej:​+Weźmy klasy z poprzedniego przykładu, rozdzielmy je na plik nagłówkowy i źródłowy. ​ 
 + 
 +Plik nagłówkowy wyglądać może tak: 
 +<code cpp> 
 +class Costam_0 { 
 +        public: 
 +                virtual void metoda ( std::string suffix ) = 0; 
 +                virtual ~Costam_0 (); 
 +}; 
 + 
 +class Costam_A : public Costam_0 { 
 +        public: 
 +                Costam_A();​ 
 +                Costam_A( std::string nazwa );  
 +                virtual void metoda ( std::string suffix );  
 +                std::string daj_nazwe () const; 
 +                void ustaw_nazwe ( const std::string nazwa );  
 +        protected:​ 
 +                std::string nazwa_; 
 +}; 
 + 
 +class Costam_B : public Costam_A { 
 +        public: 
 +                Costam_B();​ 
 +                Costam_B( std::string nazwa ); 
 +                virtual void metoda ( std::string suffix ); 
 +                Costam_B&​ operator+ ( std::string x ); 
 + 
 +                std::string x_; 
 +}; 
 +</​code>​ 
 + 
 +Plik źródłowy skompilujmy do postaci biblioteki współdzielonej:​
 <​code>​ <​code>​
 $ g++ -Wall -shared p2-shared.cpp -o p2-shared.o $ g++ -Wall -shared p2-shared.cpp -o p2-shared.o
Linia 211: Linia 287:
 } }
 </​code>​ </​code>​
-i skompilujmy go **nie zapominając** o linkowaniu z naszą biblioteką ''​p3-shared.o'':​+i skompilujmy go **nie zapominając** o linkowaniu z naszą biblioteką ''​p2-shared.o'':​
 <​code>​ <​code>​
 g++ -Wall -shared -I/​usr/​include/​boost/​ -I/​usr/​include/​python2.5/​ /​usr/​lib/​libboost_python-mt.so p2-shared.o p2.cpp -o p2.so g++ -Wall -shared -I/​usr/​include/​boost/​ -I/​usr/​include/​python2.5/​ /​usr/​lib/​libboost_python-mt.so p2-shared.o p2.cpp -o p2.so
 </​code>​ </​code>​
 I gotowe! Możemy teraz uruchomić skrypt z poprzedniego przykładu. A to wszystko bez dostępu do plików źródłowych! I gotowe! Możemy teraz uruchomić skrypt z poprzedniego przykładu. A to wszystko bez dostępu do plików źródłowych!
 +\\ (w rzeczywistej sytuacji plik p2-shared.o byłby biblioteką której źródeł nie mamy - jak widać nawet pomimo tego udało się wystawić ją jako moduł Pythona)
 +
 +==== Więcej informacji ====
 +
 +  * [[http://​www.boost.org/​libs/​python|Witryna biblioteki]]
 +  * [[http://​wiki.python.org/​moin/​boost.python|Wiki na stronie pythona]]
 +
boost_python.1208195809.txt.gz · ostatnio zmienione: 2008/04/14 19:56 przez kamituel