Strony

czwartek, 21 listopada 2019

C++ w skórze Pythona [Python3,C++]

Kilka miesięcy temu prezentowałem na blogu aplikację szyfrującą i deszyfrującą pliki metodą xor z poziomu C++ jak i pythona. Dziś pokażę wam w jaki sposób można łączyć te dwa języki.

Python jak i C++ można z powodzeniem łączyć ze sobą. Zarówno ten pierwszy osadzać w kodzie programów napisanych w C++ jak i używać bibliotek napisanych w C++ z poziomu języka python. Ja w tym artykule zajmę się jedynie uruchamianiem kodu napisanego w C++ z poziomu języka python. W opisywanym przykładach będę się posługiwał implementacja języka python napisaną w C, tak zwany CPython. Wszystkie opisywane mechanizmy dotyczą Python 3.

 

 Czy ma to sens ? 

W brew pozorom taki stan rzeczy że aplikacja nie jest pisana w jednym języku a w kilku zdarza się niezwykle często. Niejednokrotnie możemy robić to nieświadomie, na przykład programujemy grę w Pythonie wykorzystując bibliotekę pygame. Wydaje nam się że jest to czysty python, otóż nie. W rzeczywistości biblioteka Pygame to nakładka (wrapper) na bibliotekę SDL. Sama zaś biblioteka SDL została napisana w czystym C. Co niektórzy mogli by stwierdzić że sam python to nakładka na język C, jednak ja nie będę tak tego spłaszczał. Wróćmy jednak do wad i zalet łączenia kilku języków programowania w jednej aplikacji. Po pierwsze nie każdy język programowania oferuje takie same funkcjonalności czy umożliwia programowania w różnych paradygmatach. Możliwość łączenia bibliotek różnych języków daje nam olbrzymią elastyczność. Jeśli ktoś dalej mi nie wieży, weźmy na tapetę taki dość znany portal którego właścicielem jest firma sprzedająca reklamy w internecie (czyli Google).
Mam na myśli serwis YouTube. Portal ten został napisany w Pythonie. Teraz każdy kto słyszał że python jest nie wydajny, zastanawia się jak to możliwe.
Otóż wszystkie krytyczne części serwisu zostały przepisane na język wydajny zaś z python zarządza całością.

 

Biblioteka dynamiczna 

Zanim zaprezentuje jak samodzielnie napisać wrapper w Pythonie do biblioteki języka C++, omówię temat bibliotek dynamicznych.
Czym właściwie jest biblioteka dynamiczna ?
Biblioteka dynamiczna jest nazywana również biblioteką współdzieloną, z angielskiego shared library.
Słowo shared (współdzielony) nie pojawiło się w nazwie przypadkowo, kod takiej biblioteki może być wykorzystany przez kilka aplikacji. W systemie Windows biblioteki takie mają rozszerzenie .dll (Dynamic link libray) zaś w systemach rodziny UNIX rozszerzenie .so (skrót shared libray). Jeśli znasz choć trochę język C/C++ wiesz że w każdym programie musi znajdować się funkcja main od której każdy program zaczyna swoje działanie. Tą definicje pragnę w tym miejscu doprecyzować, tak w każdym programie oprócz kodów bibliotek.
Biblioteki dynamiczne to nie jedyny typ bibliotek, występują również biblioteki statycznie linkowane jednak nie będę ich tutaj omawiał. Pragnę jeszcze podkreślić że biblioteka nie jest samodzielnym programem.

 

Pakiet Ctypes 

W Pythonie jest specjalny pakiet stworzony właśnie do takich zastosowań jak integracja bibliotek napisanych w języku C z kodem Python. Z wykorzystaniem tego pakietu zadanie stworzenia wrappera jest banalnie prosto. Pakiet ten „tłumaczy nam” typy języka C na zrozumiałe dla pythona.

 

Tworzenie kodu biblioteki oraz jej kompilacja 

Zacznijmy od stworzenia pliku o nazwie: lib.cpp Za przykład posłuży nam funkcja obliczająca największy wspólny dzielnik (algorytm Euklidesa ) metodą iteracyjna. Funkcja ta będzie przyjmowała dwa argumenty typu int oraz zwracała liczbę również typu int.

Uwaga !!! 
Aby wymusić kompilacje zgodną z regułami języka C pisząc nasz program w C++ oraz  kompilując go w kompilatorze języka C++ należy funkcje które będą wywoływane z poziomu Pytona zamknąć w klauzurze extern "C". Kod biblioteki widoczny jest poniżej 




Następnie należy skompilować nasz kod źródłowy tak by stał się biblioteka dynamiczną.
Dla systemu Windows:    g++ -fPIC -shared -o lib.dll lib.cpp 
Dla systemu Linux/MacOS:  g++ -fPIC -shared -o lib.so lib.cpp 

Uwaga !!! 
Dla systemu Linux/MacOS w standardowej konfiguracji wpisanie w terminalu polecenia widocznego powyżej powinno bez żadnego problemu skompilować plik źródłowy. Dzieje się tak dlatego że zarówno Linux jak i MacOS mają domyślnie zainstalowany kompilator języka C++. System Windows wymaga zainstalowania kompilatora przez użytkownika. Dodatkowo by była możliwość kompilacji w konsoli należy dodać do zmiennej systemowej PATH ścieżkę prowadzącą do folderu bin kompilatora. 

 Jeśli kompilacja zakończyła się powodzeniem, kompilator stworzył nam plik z rozszerzeniem dll (Windows ) lub so (Linux/MacOS).


Tworzenie kodu wrappera oraz jego uruchomienie

Teraz możemy przejść do pisania kodu naszego wrappera w języku python. W tym celu stwórzmy plik o nazwie wrapper.py Przykładowy kod uruchamiający funkcje o nazwie „gcd” z naszej biblioteki dynamicznej widoczny jest poniżej.






Uwaga !!! 
Powyższy kod zadziała poprawnie tylko dla systemu Linux oraz MacOS.
Uruchamiając nasz kod na Windowsie należy zmienić linię:
lib=ctypes.CDLL('lib.so') #Linux/MacOS
na
lib=ctypes.CDLL('lib.dll') #Windows
Zapisujemy plik.
Możemy teraz uruchomić nasz plik wrappera za pomocą interperetera języka python3.
W tym celu należy w konsoli wpisać: python3 wrapper.py 

Po wywołaniu tego polecenia nasz program powinien wypisać na ekranie wartość 5. Dzieje się tak dlatego że z poziomu języka Python wywołujemy funkcje obliczającą największy wspólny dzielnik z liczb 5 i 10.

Brak komentarzy:

Prześlij komentarz