W tym samouczku pokazano, jak skonfigurować projekt pybind11 z CMake w celu zawinięcia biblioteki C++ do Python.
Ostateczny wynik będzie następujący:
C++Projekt, który możesz zbudować niezależnie odpybind11.- Biblioteka
Pythonwygenerowana w wyniku zawinięcia koduC++. - Obydwa używają
CMake.
Kod całego projektu znajdziesz tutaj.

"Źródło obrazu."
Wymagania
Oczywiście zdobądź pybind11:
conda install -c conda-forge pybind11
Utwórz projekt C++
Użyjemy zewnętrznego (bieżącego) katalogu roboczego do zbudowania Pythona i wewnętrznego katalogu o nazwie cpp do zbudowania kodu C++. Najpierw utwórz katalog C++.
mkdir cpp cd cpp
Następnie zainicjujemy projekt w C++. Dwa sposoby (z wielu innych) to:
- Używanie
VS Code. Zainstaluj rozszerzenieCMake Tools. Następnie wywołaj polecenie paleta i wybierzCMake: Quick start. Postępuj zgodnie z instrukcjami i wprowadź nazwę — wybrałemautomobile. Po wyświetleniu monitu o bibliotekę lub plik wykonywalny wybierzlibrary. Twój katalog powinien teraz wyglądać tak:
cpp/build/ cpp/motorcycle.cpp cpp/CMakeLists.txt
Oddzielimy pliki źródłowe i nagłówkowe — jest to zawsze dobra praktyka. W katalogu cpp utwórz dwa nowe katalogi:
cd cpp mkdir include mkdir src
i przenieś plik źródłowy:
mv motorcycle.cpp src/
W katalogu include chcielibyśmy mieć pojedynczy nagłówek do zaimportowania. W ten sposób moglibyśmy później po prostu #include <automobile>. Możemy to zorganizować w następujący sposób:
cd cpp/include mkdir automobile_bits touch automobile
Na koniec utwórzmy plik nagłówkowy w katalogu cpp/include/automobile_bits:
cd cpp/include/automobile_bits touch motorcycle.hpp
Ostateczna struktura katalogów powinna teraz wyglądać następująco:
cpp/build cpp/CMakeLists.txt cpp/include/automobile cpp/include/automobile_bits/motorcycle.hpp cpp/src/motorcycle.cpp
2. Alternatywnie utwórz ręcznie pliki i katalogi tak, aby ostateczna struktura była następująca:
cpp/build cpp/CMakeLists.txt cpp/include/automobile cpp/include/automobile_bits/motorcycle.hpp cpp/src/motorcycle.cpp
Będziemy musieli edytować bieżący CMakeLists.txt tak, aby mógł znaleźć pliki nagłówkowe i źródłowe. Zmodyfikowałem swój, aby brzmiał następująco:
Nadajmy także plikom motorcycle.hpp i motorcycle.cpp rozsądną zawartość. Dla nagłówka:
i źródło:
Tak! Wiem, że są głupi. Pamiętaj, że wprowadziliśmy przestrzeń nazw vehicles — to zawsze dobry pomysł.
Musimy także, aby plik nagłówkowy znalazł rzeczywistą bibliotekę. Edytuj plik include/automobile, aby przeczytać:
Możemy już zbudować bibliotekę:
- Korzystanie z wiersza poleceń:
cd cpp/build cmake .. make make install
2. Używając swojego ulubionego IDE, np. XCode:
cd cpp/build cmake .. -GXcode
powinien wygenerować automobile.xcodeproject w katalogu build.
Tak czy inaczej, powinieneś pobrać bibliotekę do zbudowania i zainstalowania.
Testowanie biblioteki C++
Zanim przejdziemy do pakowania biblioteki w Python, utwórzmy test dla biblioteki C++ (nie prawdziwy test, po prostu miejsce, w którym możemy się pobawić!).
Utwórz nowy katalog w cpp:
cd cpp mkdir tests
Tutaj ponownie skonfigurujemy projekt CMake dla naszego testu. Spraw, aby struktura katalogów wyglądała następująco:
cpp/tests/CMakeLists.txt cpp/tests/src/test.cpp
Edytuj plik test.cpp, aby przeczytać:
i edytuj plik CMakeLists.txt:
Utwórz i uruchom tego złego chłopca, używając XCode jak poprzednio, lub z wiersza poleceń:
mkdir build cd build cmake .. make cd ../bin ./test
Zauważ, że plik binarny będzie znajdować się w katalogu bin. Dane wyjściowe powinny być następujące:
Made a motorcycle called: Yamaha Zoom Zoom on road: mullholland
Konfigurowanie opakowania Pythona
Na koniec przejdźmy do pakowania biblioteki w Pythona. Przenosimy katalog w górę! W katalogu głównym utwórzmy nowy katalog o nazwie python. Będzie przechowywać cały kod kleju:
mkdir python
Potrzebujemy także pliku CMakeLists.txt o zawartości:
Powinieneś być gotowy na zbudowanie swojej biblioteki Python! Próbować:
mkdir build cd build cmake .. -DPYTHON_LIBRARY_DIR=”/path/to/site-packages” -DPYTHON_EXECUTABLE=”/path/to/executable/python3" make make install
Jak zwykle, możesz także wygenerować kod za pomocą generatora dla swojego ulubionego IDE, np. dodając -GXcode do polecenia cmake. Moje ścieżki były:
DPYTHON_LIBRARY_DIR=”/Users/USERNAME/opt/anaconda3/lib/python3.7/site-packages” DPYTHON_EXECUTABLE=”/Users/USERNAME/opt/anaconda3/bin/python3"
Pamiętaj, że jeśli jesteś leniwy tak jak ja, możesz spróbować dodać do testowania:
set(PYTHON_LIBRARY_DIR “/Users/USERNAME/opt/anaconda3/lib/python3.7/site-packages”) set(PYTHON_EXECUTABLE “/Users/USERNAME/opt/anaconda3/bin/python3”)
w twoim CMakeLists.txt — oczywiście nie jest to dobry trik na produkcję!
Uruchom python (upewnij się, że jest taki sam, jak określono w PYTHON_EXECUTABLE powyżej) i spróbuj:
>>> import automobile Traceback (most recent call last): File “<stdin>”, line 1, in <module> ImportError: dynamic module does not define module export function (PyInit_automobile)
Masz niezły, gruby błąd, ale to jest OK! Nie napisaliśmy jeszcze kodu kleju, ale przynajmniej Twój CMake działa i Python może znaleźć Twoją bibliotekę.
Opakowanie biblioteki w Pythonie
Teraz o właściwej logice zawinięcia kodu C++ w Python. Odbędzie się to w katalogu python. Najpierw utwórz plik, który będzie definiował „funkcję eksportu modułu”, na którą python narzekał w ostatniej części:
touch python/automobile.cpp
Nadaj mu następującą treść:
Następnie zdefiniujemy zadeklarowaną metodę init_motorcycle. Zrobimy to w osobnym pliku:
touch python/motorcycle.cpp
Edytuj, aby przeczytać:
Zawsze uważam, że sam kod jest najlepszym wyjaśnieniem, ale kilka wskazówek:
”Motorcycle”definiuje nazwę klasy wPython— możesz ją zmienić, jeśli chcesz! Zwróć także uwagę na wygląd przestrzeni nazw..def(py::init<std::string>(), py::arg(“name”))definiuje konstruktora.py::arg(“name”)pozwala na użycie nazwanych argumentów wPython..def(“get_name”, py::overload_cast<>( &vehicles::Motorcycle::get_name, py::const_))otacza metodęget_name. Zwróć uwagę na sposób pakowania deklaracjiconst..def(“ride”, py::overload_cast<std::string>( &vehicles::Motorcycle::ride, py::const_), py::arg(“road”));otacza metodęride. Argumenty metody są zadeklarowane wpy::overload_cast<std::string>(oddzielone przecinkami, jeśli jest ich wiele) i ponownie można je nazwać za pomocąpy::arg(“road”). Zwróć także uwagę na średnik na końcu — często zapominany, ale powinien to być prawidłowy kodC++.
Możesz teraz przetestować swoją bibliotekę. Uruchom ponownie make i make install, aby odbudować i zainstalować bibliotekę.
Uruchom python i wypróbuj:
powinien dać taki sam wynik jak poprzednio:
Made a motorcycle called: Yamaha Zoom Zoom on road: mullholland
Możesz utworzyć inny skrypt testowy z tą zawartością i umieścić go w katalogu tests/test.py.
Wniosek
To wszystko w tym samouczku. Pełny kod znajdziesz tutaj.
Zaletą tej konfiguracji jest to, że możesz spokojnie zbudować swój projekt C++ z katalogu cpp, a na końcu w warstwie zewnętrznej martwić się o zawinięcie go do Python.
Dziękuje za przeczytanie!