C++ feat. Python Подключение, встраивание, установка с легкостью

C++ feat. Python Easy Connection, Embedding, and Installation

Приложение предприятия на C++ для Windows выполняет модуль Python. Вот мое путешествие через этот процесс технологического слияния, от первой строки кода до доставки приложения.

Изображение, сгенерированное автором с помощью инструментов искусственного интеллекта

Введение

Простота Python, обширная экосистема пакетов и поддерживающее сообщество делают его привлекательным выбором. Однако, когда речь идет о сложных пользовательских интерфейсах, многопоточных потоках данных и круглосуточной надежности в корпоративных решениях, главную роль играет C++. Иногда возникает необходимость объединить эти два языка, стремясь получить лучшее от обоих миров.

Часто код Python вызывает методы C++ для сложных алгоритмических вычислений. Однако я столкнулся с обратной ситуацией, когда мое приложение машинного обучения срочно требовало вызова пользовательской модели с кодом вывода, написанным на Python. Переписывать его на C++ или использовать соответствующий движок вывода было невозможно. Моя первоначальная мысль была проста: “Вызов Python из C++ должен быть легким.” К сожалению, я недооценил сложность этого процесса!

Ожидания и реальность

Существует несколько готовых мостов для взаимодействия между C++ и Python:

  1. Встроенный интерфейс C++ Python, доступный через заголовочные файлы и библиотеки C++ (Python.h, Python.lib), поставляемые вместе со стандартным пакетом Python.
  2. Boost.Python, для тех, кто знаком с библиотекой Boost.
  3. Pybind11, мой выбор благодаря его мощи и легкости интеграции в проекты.

На первый взгляд решение кажется близким, и остается только выбрать наиболее подходящий мост из этой тройки. Однако давайте рассмотрим это поближе!

Упомянутые методы прекрасно справляются со своей основной задачей – обеспечением взаимодействия между Python и C++, что достаточно для предварительных испытаний и экспериментов. Однако, при построении готового решения мы должны думать о полном жизненном цикле программного обеспечения. И одна из проблем, которую нужно решить, это распространение. Чтобы прояснить, задача заключается в распространении бинарных файлов C++ вместе с компонентами Python. Даже само распространение кода Python может быть сложным, если учесть установку интерпретатора в операционной системе. Более того, могут существовать несколько версий Python, и для каждого дополнительного пакета необходима правильная установка, включая разрешение зависимостей. Вопрос заключается в следующем: какие варианты подходят для нашего случая?

  1. Инструктировать пользователей установить Python и необходимые пакеты перед использованием C++ приложения.
  2. Включить установщик Python в установщик C++ для автоматического решения связанных проблем, включая другие установки Python на компьютере пользователя.

Ни один из вариантов не является оптимальным. В результате я отправился на поиски более надежного и стабильного подхода, который с удовольствием хочу поделиться с вами.

Встроенный Python

Во время вашего последнего посещения домашней страницы Python вы исследовали варианты распространения? Среди них находится интересный выбор: встроенная пакетная версия для Windows. Этот вариант привлек мое внимание, представляя собой, казалось бы, волшебное решение для проблемы распространения. Изучая официальную документацию, я обнаружил подробности об этой встроенной версии – удивительно, Python предлагает встроенную версию для безшовной интеграции в другие приложения.

Исследуя содержимое архива встроенной версии, возникают пара вопросов: Как добавить дополнительные модули в эту установку? И как направить C++ приложение на использование этой встроенной версии?

Ответ на первый вопрос относительно прост: пакеты могут быть просто скопированы из стандартной установки Python.

Ответ на второй вопрос потребовал обширного исследования, так как упомянутые мосты Python работают с установкой по умолчанию. Изменение переменных среды Python (PYTHONPATH, PYTHONHOME) может быть иногда полезным, но это не является окончательным решением для перехода к встроенной установке, так как Python может размещать свои файлы в разных местах операционной системы.

Флаги Python для C++

Простейшая программа на Python может содержать всего одну строку:

print(‘Привет, Мир!’)

Python защищает программистов от множества внутренних процедур для достижения этой видимой простоты. При скрытом запуске этой строки интерпретатор обрабатывает параметры установки, инициализацию, импорт модулей по умолчанию, компиляцию байт-кода, поиск функций и многое другое. Однако есть способ вмешаться в эти скрытые механизмы. Цитируя официальную документацию “Python имеет переменные для глобальной конфигурации, чтобы управлять различными функциями и параметрами”. Они существуют в виде аргументов командной строки или флагов и методов для C++-кода, доступных через уже упомянутый Python.h. В данном случае нам нужно переопределить путь поиска Python с помощью метода Py_SetPath(). Другие флаги используются в некоторых необычных случаях: Py_NoSiteFlag, Py_NoUserSiteDirectory, Py_IgnoreEnvironmentFlag. Эти флаги относятся к пропуску загрузки модуля site по умолчанию или к игнорированию отдельного каталога с модулями в домашнем каталоге пользователя. Изучите их, чтобы обработать определенные случаи!

Руководство по краткому изложению

  1. Подготовьте свой проект на C++ к началу интеграции с Python.
  2. Решите, какую версию Python использовать, и установите ее в качестве стандартной установки в Windows.
  3. Скачайте ту же версию Python в виде встроенного пакета и извлеките его в каталог вашего проекта.
  4. Напишите тестовый код на Python, который вызывает все необходимые методы Python для дальнейшего копирования модулей и тестирования.
  5. Запустите тестовый код на Python с встроенной установкой (используйте флаги командной строки для переопределения пути поиска). Если используются непо умолчанию модули, вы столкнетесь с ошибками отсутствующих модулей. Последовательно скопируйте недостающие модули с установки по умолчанию во встроенную установку, чтобы тестовый код Python успешно завершился.
  6. Перейдите к вашему проекту на C++. Получите pybind11. Добавьте проекты, которые включают путь поиска к нему.
  7. Добавьте пути поиска заголовков и библиотек из установки Python по умолчанию в ваш проект.
  8. В C++ коде используйте Py_SetPath() для установки относительного пути к встроенной установке Python. Используйте дополнительные флаги, если необходимо.
  9. Если один из пакетов Python находится в пользовательском каталоге site-package, просто скопируйте его в папку встроенного пакета и добавьте отдельный путь к нему в Py_SetPath().
  10. В C++ коде используйте API pybind11 для доступа к методам Python. Если C++ программа зависает при вызове метода Python, скорее всего некоторый модуль Python не может быть импортирован – используйте тестовый код Python из шага [5], чтобы решить эту проблему.
  11. Распространяйте C++ бинарные файлы с встроенной папкой Python в качестве неотъемлемой части. Вам также может понадобиться скопировать некоторые DLL-файлы в место расположения бинарных файлов, такие как python3.dll и Windows redistributables.

Заключительная часть

Производственные приложения на C++ могут включать встроенный Python как часть без того, чтобы пользователи об этом знали. И это великое достижение, поскольку мы все любим выполнять свои задачи с помощью простых, но мощных приложений, а не гадать над их установкой!

Я делюсь своим демонстрационным проектом, который включает все идеи из статьи для вашего быстрого старта.