В этом руководстве показано, как настроить проект pybind11
с CMake
для обертывания библиотеки C++
в Python
.
Конечный результат будет:
C++
проект, который можно построить независимо отpybind11
.- Библиотека
Python
, созданная путем упаковки кодаC++
. - Оба используют
CMake
.
Здесь вы можете найти код для всего проекта.
Требования
Очевидно, получаем pybind11
:
conda install -c conda-forge pybind11
Создайте проект C ++
Мы будем использовать внешний (текущий) рабочий каталог для сборки Python и внутренний каталог с именем cpp
для сборки кода C++
. Сначала создайте каталог C++
.
mkdir cpp cd cpp
Затем мы инициализируем проект C ++. Двумя способами (и их гораздо больше) являются:
- Используя
VS Code
. Установите расширениеCMake Tools
. Затем откройте палитру команд и выберитеCMake: Quick start
. Следуйте подсказкам и введите имя - я выбралautomobile
. Когда будет предложено выбрать библиотеку или исполняемый файл, выберитеlibrary
. Теперь ваш каталог должен выглядеть так:
cpp/build/ cpp/motorcycle.cpp cpp/CMakeLists.txt
Мы разделим исходный и заголовочный файлы - это всегда хорошая практика. В каталоге cpp
создайте два новых каталога:
cd cpp mkdir include mkdir src
и переместите исходный файл:
mv motorcycle.cpp src/
В каталоге include мы хотели бы иметь один заголовок для импорта. Таким образом, позже мы могли бы просто #include <automobile>
. Мы можем организовать это следующим образом:
cd cpp/include mkdir automobile_bits touch automobile
Наконец, давайте создадим файл заголовка в каталоге cpp/include/automobile_bits
:
cd cpp/include/automobile_bits touch motorcycle.hpp
Окончательная структура каталогов должна теперь выглядеть так:
cpp/build cpp/CMakeLists.txt cpp/include/automobile cpp/include/automobile_bits/motorcycle.hpp cpp/src/motorcycle.cpp
2. В качестве альтернативы, вручную создайте файлы и каталоги, чтобы окончательная структура была следующей:
cpp/build cpp/CMakeLists.txt cpp/include/automobile cpp/include/automobile_bits/motorcycle.hpp cpp/src/motorcycle.cpp
Нам нужно будет отредактировать текущий CMakeLists.txt
, чтобы он мог найти заголовочные и исходные файлы. Я отредактировал свой, чтобы он читался следующим образом:
Давайте также добавим файлам motorcycle.hpp
и motorcycle.cpp
разумное содержание. Для заголовка:
и источник:
Да! Я знаю, что они тупые. Обратите внимание, что мы ввели пространство имен vehicles
- это всегда хорошая идея.
Нам также нужно, чтобы файл заголовка находил актуальную библиотеку. Отредактируйте include/automobile
файл следующим образом:
Теперь мы уже можем собрать библиотеку:
- Используя командную строку:
cd cpp/build cmake .. make make install
2. Используя вашу любимую IDE, например XCode
:
cd cpp/build cmake .. -GXcode
должен сгенерировать automobile.xcodeproject
в каталоге build
.
В любом случае вы должны получить библиотеку для сборки и установки.
Тестирование библиотеки C ++
Прежде чем мы перейдем к обертке библиотеки в Python
, давайте создадим тест для библиотеки C++
(не настоящий тест, просто где-нибудь, чтобы мы могли возиться!).
Создайте новый каталог в cpp
:
cd cpp mkdir tests
Здесь мы снова создадим CMake
проект для нашего теста. Сделайте структуру каталогов такой:
cpp/tests/CMakeLists.txt cpp/tests/src/test.cpp
Отредактируйте файл test.cpp
, чтобы он читался:
и отредактируйте CMakeLists.txt
файл:
Создайте и запустите этого плохого парня, используя XCode
, как раньше, или из командной строки:
mkdir build cd build cmake .. make cd ../bin ./test
Обратите внимание, что двоичный файл будет в bin
directory. Результат должен быть:
Made a motorcycle called: Yamaha Zoom Zoom on road: mullholland
Настройка оболочки Python
Наконец, давайте перейдем к обертке библиотеки на Python. Мы движемся вверх по каталогу! В главном каталоге создадим новый каталог с именем python
. Он будет содержать весь код клея:
mkdir python
Также нам понадобится CMakeLists.txt
файл с содержимым:
Вы должны быть готовы создать свою Python
библиотеку! Пытаться:
mkdir build cd build cmake .. -DPYTHON_LIBRARY_DIR=”/path/to/site-packages” -DPYTHON_EXECUTABLE=”/path/to/executable/python3" make make install
Как обычно, вы также можете сгенерировать код, используя генератор для вашей любимой IDE, например добавив -GXcode
к команде cmake
. Мои пути были:
DPYTHON_LIBRARY_DIR=”/Users/USERNAME/opt/anaconda3/lib/python3.7/site-packages” DPYTHON_EXECUTABLE=”/Users/USERNAME/opt/anaconda3/bin/python3"
Обратите внимание: если вы, как я, ленивы, вы можете попробовать добавить для тестирования:
set(PYTHON_LIBRARY_DIR “/Users/USERNAME/opt/anaconda3/lib/python3.7/site-packages”) set(PYTHON_EXECUTABLE “/Users/USERNAME/opt/anaconda3/bin/python3”)
в вашем CMakeLists.txt
- явно не удачный трюк для продакшена!
Запустите python
(убедитесь, что он такой же, как вы указали в PYTHON_EXECUTABLE
выше) и попробуйте:
>>> import automobile Traceback (most recent call last): File “<stdin>”, line 1, in <module> ImportError: dynamic module does not define module export function (PyInit_automobile)
У вас хорошая жирная ошибка, но ничего страшного! Мы еще не написали связующий код, но по крайней мере ваш CMake
работает и Python
может найти вашу библиотеку.
Оборачивание библиотеки в Python
Теперь о самой логике обертывания кода C++
в Python
. Он будет размещен в каталоге python
. Сначала создайте файл, который будет определять «функцию экспорта модуля», о которой python
говорил в предыдущей части:
touch python/automobile.cpp
Дайте ему следующее содержание:
Далее мы определим объявленный метод init_motorcycle. Сделаем это в отдельном файле:
touch python/motorcycle.cpp
Отредактируйте его так:
Я всегда считаю, что сам код является лучшим объяснением, но несколько указателей:
”Motorcycle”
определяет имя класса вPython
- вы можете изменить его, если хотите! Обратите внимание также на внешний вид пространства имен..def(py::init<std::string>(), py::arg(“name”))
определяет конструктор.py::arg(“name”)
позволяет использовать именованные аргументы вPython
..def(“get_name”, py::overload_cast<>( &vehicles::Motorcycle::get_name, py::const_))
является оболочкой для методаget_name
. Обратите внимание, как завернуто объявлениеconst
..def(“ride”, py::overload_cast<std::string>( &vehicles::Motorcycle::ride, py::const_), py::arg(“road”));
является оболочкой для методаride
. Аргументы метода объявлены вpy::overload_cast<std::string>
(разделены запятыми, если их несколько), и их можно снова назвать с помощьюpy::arg(“road”)
. Также обратите внимание на точку с запятой в конце - часто забывают, но это должен быть правильныйC++
код.
Теперь вы можете протестировать свою библиотеку. Запустите make
и make install
еще раз, чтобы перестроить и установить библиотеку.
Запустите python
и попробуйте:
должен дать тот же результат, что и раньше:
Made a motorcycle called: Yamaha Zoom Zoom on road: mullholland
Вы можете создать другой тестовый сценарий с этим содержимым, расположенный в каталоге tests/test.py
.
Заключение
Это все для этого урока. Вы можете найти полный код здесь.
Приятная часть этой настройки заключается в том, что вы можете спокойно собрать свой C++
проект из каталога cpp
, а затем, в конце внешнего слоя, беспокоиться о том, чтобы обернуть его в Python
.
Спасибо за прочтение!