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

Next revision Both sides next revision
boost_python [2008/04/14 18:54]
kamituel utworzono
boost_python [2008/04/14 19:56]
kamituel
Linia 23: Linia 23:
 } }
 </​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 '​.'​ znajduje się w domyślnej ścieżce poszukiwania Pythona).\\ 
 <code python> <code python>
 import p1 import p1
Linia 69: Linia 69:
                 Costam_B( std::string nazwa ) : Costam_A(nazwa) {}                 Costam_B( std::string nazwa ) : Costam_A(nazwa) {}
                 virtual void metoda ( std::string suffix ) { nazwa_ += " B " + suffix; }                 virtual void metoda ( std::string suffix ) { nazwa_ += " B " + suffix; }
 +                Costam_B&​ operator+ ( std::string x ) { nazwa_ += " ++ " + x; return *this; }
  
                 std::string x_;                 std::string x_;
 }; };
 </​code>​ </​code>​
-Widzimy trzy klasy. Abstrakcyjną Costam_0, Costam_A po niej dziedziczącą oraz Costam_B dziedziczącą po Costam_A. Jak taką hierarchię udostępnić jako moduł Pythona?+Widzimy trzy klasy. Abstrakcyjną ​''​Costam_0''​''​Costam_A'' ​po niej dziedziczącą oraz ''​Costam_B'' ​dziedziczącą po ''​Costam_A''​. Jak taką hierarchię udostępnić jako moduł Pythona?
  
-Aby udostępnić klasę Costam_0 należy dopisać kod tego typu:+Aby udostępnić klasę ​''​Costam_0'' ​należy dopisać kod tego typu:
 <code cpp> <code cpp>
 class Costam_0Wrap : public Costam_0, public wrapper<​Costam_0>​ class Costam_0Wrap : public Costam_0, public wrapper<​Costam_0>​
Linia 92: Linia 93:
 } }
 </​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 właściwie 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ę 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.
  
-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:​
 <code cpp> <code cpp>
         class_<​Costam_A,​ bases<​Costam_0> ​ >​("​CostamA"​)         class_<​Costam_A,​ bases<​Costam_0> ​ >​("​CostamA"​)
Linia 103: Linia 104:
  
 </​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'' ​''​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 ten sposób:
Linia 113: Linia 114:
                 .def_readonly("​x_ro",​ &​Costam_B::​x_)                 .def_readonly("​x_ro",​ &​Costam_B::​x_)
                 .def_readwrite("​x_rw",​ &​Costam_B::​x_)                 .def_readwrite("​x_rw",​ &​Costam_B::​x_)
 +                .def(self + std::​string())
                 ;                 ;
 </​code>​ </​code>​
 Ponownie, nie definiujemy jeszcze raz elementów z klas stojących wyżej w hierarchii - zajmujemy się tylko nowymi składowymi. Ponownie, nie definiujemy jeszcze raz elementów z klas stojących wyżej w hierarchii - zajmujemy się tylko nowymi składowymi.
-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 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//.
  
 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.
 +
 +Skoro mamy już gotowy drugi moduł, przetestujmy go:
 +<code python>
 +#​!/​usr/​bin/​python
 +import p2
 +
 +# Odziedziczylismy po klasie Costam_B, przeciazajac metode '​metoda'​
 +class CostamC(p2.CostamB):​
 + def metoda(self,​ suffix):
 + self.nazwa_prop += " C " + suffix ​
 +
 +
 +# Stworzmy obiekty kazdej z klas:
 +a = p2.CostamA("​AAA"​)
 +b = p2.CostamB("​BBB"​)
 +c = CostamC("​CCC"​)
 +
 +# To nie zadziala! Konstruktor bezparametrowy nie zostal udostepniony
 +# b_prim = p2.CostamB()
 +
 +# Wolamy metode '​metoda'​. Jako ze jest ona wirtualna, ​
 +# powinnismy uzyskac nieco inny efekt za kazdym razem...
 +a.metoda("​aaa"​);​
 +b.metoda("​bbb"​);​
 +c.metoda("​ccc"​);​
 +
 +# ... i tak faktycznie jest:
 +print a.nazwa_prop
 +print b.nazwa_prop
 +print c.nazwa_prop
 +
 +# Przetestujmy takze operator dodawania dla klasy CostamB
 +# (zadziala takze dla klasy CostamC jako ze dziedziczy po CostamB)
 +b = b + "PLUS DZIALA" ​
 +print b.nazwa_prop
 +
 +# Zostaly jeszcze pola read-only i read-write dla zmiennej x:
 +b.x_rw = "​Zapisalo sie!" ​
 +print b.x_rw
 +print b.x_ro
 +# Ale to nie zadziala: x_ro jest tylko do odczytu!
 +#b.x_ro = "To sie nie zapisze!" ​
 +</​code>​
 +
 +==== Z życia wzięte ====
 +Czyli działa - po co jednak aż tak się gimnastykować skoro można napisać te klasy od razu w Pythonie?
 +Powodów jest kilka:
 +  * 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 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.
 +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:​
 +<​code>​
 +$ g++ -Wall -shared p2-shared.cpp -o p2-shared.o
 +</​code>​
 +Teraz stwórzmy plik ''​p2.cpp''​ z definicją naszego modułu Pythona:
 +<code cpp>
 +#include "​p2-shared.h"​
 +#include <​boost/​python.hpp>​
 +using namespace boost::​python;​
 +
 +class Costam_0Wrap : public Costam_0, public wrapper<​Costam_0>​
 +{
 +        virtual void metoda ( std::string suffix ) {
 +                this->​get_override("​metoda"​)();​
 +        }
 +};
 +
 +
 +
 +BOOST_PYTHON_MODULE(p2) {
 +        class_<​Costam_0Wrap,​ boost::​noncopyable>​("​Costam0"​)
 +                .def("​metoda",​ pure_virtual(&​Costam_0::​metoda))
 +                ;
 +        class_<​Costam_A,​ bases<​Costam_0> ​ >​("​CostamA"​)
 +                .def(init<​std::​string>​())
 +                .add_property("​nazwa_prop",​ &​Costam_A::​daj_nazwe,​ &​Costam_A::​ustaw_nazwe)
 +                ;
 +        class_<​Costam_B,​ bases<​Costam_A>​ >​("​CostamB",​ init<​std::​string>​())
 +                .def_readonly("​x_ro",​ &​Costam_B::​x_)
 +                .def_readwrite("​x_rw",​ &​Costam_B::​x_)
 +                .def(self + std::​string())
 +                ;
 +}
 +</​code>​
 +i skompilujmy go **nie zapominając** o linkowaniu z naszą biblioteką ''​p3-shared.o'':​
 +<​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
 +</​code>​
 +I gotowe! Możemy teraz uruchomić skrypt z poprzedniego przykładu. A to wszystko bez dostępu do plików źródłowych!
boost_python.txt · ostatnio zmienione: 2008/04/16 07:24 przez kamituel