5. Создание расширений C и C++ в Windows¶
В этой главе кратко объясняется, как создать модуль расширения Windows для Python с помощью Microsoft Visual C++, а затем приводится более подробная справочная информация о том, как это работает. Объяснительный материал будет полезен как Windows-программисту, изучающему создание расширений Python, так и Unix-программисту, заинтересованному в создании программного обеспечения, которое может быть успешно создано как на Unix, так и на Windows.
Авторам модулей рекомендуется использовать подход distutils для создания модулей расширения, а не тот, который описан в этом разделе. Вам все равно понадобится компилятор C, который использовался для сборки Python; обычно это Microsoft Visual C++.
Примечание
В этой главе упоминается ряд имен файлов, которые содержат закодированный номер версии Python. Эти имена файлов представлены с номером версии, показанным как XY
; на практике 'X'
будет основным номером версии, а 'Y'
- минорным номером версии Python, с которой вы работаете. Например, если вы используете Python 2.2.1, XY
на самом деле будет 22
.
5.1. Подход к созданию кулинарной книги¶
В Windows, как и в Unix, существует два подхода к сборке модулей расширения: использовать пакет distutils
для управления процессом сборки или делать все вручную. Подход distutils хорошо работает для большинства расширений; документация по использованию distutils
для сборки и упаковки модулей расширения доступна в Распространение модулей Python (устаревшая версия). Если вы обнаружите, что вам действительно нужно все делать вручную, может быть полезно изучить файл проекта для модуля стандартной библиотеки winsound.
5.2. Различия между Unix и Windows¶
Unix и Windows используют совершенно разные парадигмы для загрузки кода во время выполнения. Прежде чем пытаться создать модуль с возможностью динамической загрузки, узнайте, как работает ваша система.
В Unix файл разделяемого объекта (.so
) содержит код, который будет использоваться программой, а также имена функций и данных, которые она ожидает найти в программе. Когда файл присоединяется к программе, все ссылки на эти функции и данные в коде файла изменяются, чтобы указывать на фактические места в программе, где функции и данные размещаются в памяти. По сути, это и есть операция соединения.
В Windows файл библиотеки динамических связей (.dll
) не имеет висячих ссылок. Вместо этого обращение к функциям или данным происходит через таблицу поиска. Таким образом, код DLL не нужно исправлять во время выполнения, чтобы он ссылался на память программы; вместо этого код уже использует таблицу поиска DLL, а таблица поиска изменяется во время выполнения, чтобы указать на функции и данные.
В Unix существует только один тип библиотечных файлов (.a
), которые содержат код из нескольких объектных файлов (.o
). На этапе компоновки для создания общего объектного файла (.so
) компоновщик может обнаружить, что он не знает, где определен идентификатор. Компоновщик будет искать его в объектных файлах библиотек; если он найдет его, то включит весь код из этого объектного файла.
В Windows существует два типа библиотек: статическая библиотека и библиотека импорта (обе называются .lib
). Статическая библиотека похожа на файл Unix .a
; она содержит код, который нужно включать по мере необходимости. Библиотека импорта в основном используется только для того, чтобы убедить компоновщика, что определенный идентификатор легален и будет присутствовать в программе, когда DLL будет загружена. Таким образом, компоновщик использует информацию из библиотеки импорта для построения таблицы поиска для использования идентификаторов, которые не включены в DLL. При компоновке приложения или DLL может быть создана библиотека импорта, которую необходимо будет использовать для всех будущих DLL, зависящих от символов в приложении или DLL.
Предположим, вы создаете два модуля с динамической загрузкой, B и C, которые должны совместно использовать один блок кода A. В Unix вы не передадите A.a
компоновщику для B.so
и C.so
; это приведет к тому, что он будет включен дважды, так что у B и C будет своя копия. В Windows сборка A.dll
также приведет к сборке A.lib
. Вы до передаете A.lib
компоновщику для B и C. A.lib
не содержит кода; он просто содержит информацию, которая будет использоваться во время выполнения для доступа к коду A.
В Windows использование библиотеки импорта подобно использованию import spam
; оно дает вам доступ к именам спама, но не создает отдельной копии. В Unix связывание с библиотекой больше похоже на from spam import *
; при этом создается отдельная копия.
5.3. Использование DLL на практике¶
Windows Python построен на Microsoft Visual C++; использование других компиляторов может работать или не работать. Остальная часть этого раздела посвящена MSVC++.
При создании DLL в Windows вы должны передать компоновщику pythonXY.lib
. Для создания двух DLL, spam и ni (которая использует функции языка C, найденные в spam), можно использовать следующие команды:
cl /LD /I/python/include spam.c ../libs/pythonXY.lib
cl /LD /I/python/include ni.c spam.lib ../libs/pythonXY.lib
Первая команда создала три файла: spam.obj
, spam.dll
и spam.lib
. Spam.dll
не содержит никаких функций Python (таких как PyArg_ParseTuple()
), но он знает, как найти код Python, благодаря pythonXY.lib
.
Вторая команда создала ni.dll
(и .obj
и .lib
), которая знает, как найти нужные функции из спама, а также из исполняемого файла Python.
Не каждый идентификатор экспортируется в таблицу поиска. Если вы хотите, чтобы другие модули (включая Python) могли видеть ваши идентификаторы, вы должны сказать _declspec(dllexport)
, как в void _declspec(dllexport) initspam(void)
или PyObject _declspec(dllexport) *NiGetSpamData(void)
.
Developer Studio добавит множество библиотек импорта, которые на самом деле вам не нужны, и добавит около 100K к вашему исполняемому файлу. Чтобы избавиться от них, используйте диалог Project Settings, вкладка Link, чтобы указать ignore default libraries. Добавьте правильную msvcrtxx.lib
в список библиотек.