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 (версия Legacy). Если вы обнаружите, что вам действительно нужно все делать вручную, может быть полезно изучить файл проекта для модуля стандартной библиотеки 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
в список библиотек.