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, существует два подхода к сборке модулей расширения: использовать пакет setuptools для управления процессом сборки или делать все вручную. Подход с использованием setuptools хорошо работает для большинства расширений; документация по использованию setuptools для сборки и упаковки модулей расширения доступна в Создание расширений C и C++ с помощью setuptools. Если вы обнаружите, что вам действительно нужно все делать вручную, может быть полезно изучить файл проекта для модуля стандартной библиотеки 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 в список библиотек.