5. Construindo Extensões C e C++ no Windows

Este capítulo explica brevemente como criar um módulo de extensão do Windows para Python usando o Microsoft Visual C++ e segue com informações mais detalhadas sobre como ele funciona. O material explicativo é útil para o programador do Windows aprender a construir extensões Python e o programador Unix interessado em produzir software que possa ser construído com sucesso no Unix e no Windows.

Os autores de módulos são encorajados a usar a abordagem distutils para construir módulos de extensão, em vez daquele descrito nesta seção. Você ainda precisará do compilador C que foi usado para construir o Python; normalmente o Microsoft Visual C++.

Nota

Este capítulo menciona vários nomes de arquivos que incluem um número de versão do Python codificado. Esses nomes de arquivos são representados com o número da versão mostrado como XY; na prática, 'X' será o número da versão principal e 'Y' será o número da versão secundária da versão do Python com a qual você está trabalhando. Por exemplo, se você estiver usando o Python 2.2.1, XY será 22.

5.1. Uma abordagem de livro de receitas

Existem duas abordagens para construir módulos de extensão no Windows, assim como no Unix: use o pacote distutils para controlar o processo de construção ou faça as coisas manualmente. A abordagem distutils funciona bem para a maioria das extensões; documentação sobre o uso de distutils para construir e empacotar módulos de extensão está disponível em Distribuindo Módulos Python (Versão legada). Se você achar que realmente precisa fazer as coisas manualmente, pode ser instrutivo estudar o arquivo do projeto para o módulo de biblioteca padrão winsound.

5.2. Diferenças entre o Unix e o Windows

O Unix e o Windows usam paradigmas completamente diferentes para o carregamento do código em tempo de execução. Antes de tentar construir um módulo que possa ser carregado dinamicamente, esteja ciente de como o seu sistema funciona.

No Unix, um arquivo de objeto compartilhado (.so) contém código a ser usado pelo programa e também os nomes de funções e dados que ele espera encontrar no programa. Quando o arquivo é associado ao programa, todas as referências a essas funções e dados no código do arquivo são alteradas para apontar para os locais reais no programa em que as funções e os dados são colocados na memória. Isso é basicamente uma operação de vinculação.

No Windows, um arquivo de biblioteca de vínculo dinâmico (.dll) não possui referências pendentes. Em vez disso, um acesso a funções ou dados passa por uma tabela de pesquisa. Portanto, o código DLL não precisa ser corrigido no tempo de execução para se referir à memória do programa; em vez disso, o código já usa a tabela de pesquisa da DLL e a tabela de pesquisa é modificada em tempo de execução para apontar para as funções e dados.

No Unix, existe apenas um tipo de arquivo de biblioteca (.a) que contém código de vários arquivos de objetos (.o). Durante a etapa da vinculação para criar um arquivo de objeto compartilhado (.so), o vinculador pode achar que não sabe onde um identificador está definido. O vinculador procurará nos arquivos de objeto nas bibliotecas; se encontrar, incluirá todo o código desse arquivo de objeto.

No Windows, existem dois tipos de biblioteca, uma biblioteca estática e uma biblioteca de importação (ambas chamadas .lib). Uma biblioteca estática é como um arquivo Unix .a; contém código a ser incluído conforme necessário. Uma biblioteca de importação é basicamente usada apenas para garantir ao vinculador que um determinado identificador é legal e estará presente no programa quando a DLL for carregada. Portanto, o vinculador usa as informações da biblioteca de importação para construir a tabela de pesquisa para o uso de identificadores que não estão incluídos na DLL. Quando uma aplicação ou uma DLL é vinculado, pode ser gerada uma biblioteca de importação, que precisará ser usada para todas as DLLs futuras que dependem dos símbolos na aplicação ou DLL.

Suponha que você esteja construindo dois módulos de carregamento dinâmico, B e C, que devem compartilhar outro bloco de código A. No Unix, você não passaria A.a ao ligador para B.so e C.so; isso faria com que fosse incluído duas vezes, para que B e C tivessem sua própria cópia. No Windows, a construção A.dll também construirá A.lib. Você passa A.lib ao ligador para B e C. A.lib não contém código; apenas contém informações que serão usadas em tempo de execução para acessar o código de A.

No Windows, usar uma biblioteca de importação é como usar import spam; fornece acesso aos nomes de spam, mas não cria uma cópia separada. No Unix, vincular a uma biblioteca é mais como from spam import *; ele cria uma cópia separada.

5.3. Usando DLLs na prática

O Python para Windows é criado no Microsoft Visual C++; o uso de outros compiladores pode ou não funcionar. O restante desta seção é específico do MSVC++.

Ao criar DLLs no Windows, você deve passar pythonXY.lib para o ligador. Para construir duas DLLs, spam e ni (que usa funções C encontradas em spam), você pode usar estes comandos:

cl /LD /I/python/include spam.c ../libs/pythonXY.lib
cl /LD /I/python/include ni.c spam.lib ../libs/pythonXY.lib

O primeiro comando criou três arquivos: spam.obj, spam.dll e spam.lib. O spam.dll não contém nenhuma função Python (como PyArg_ParseTuple()), mas sabe como encontrar o código Python graças a pythonXY.lib.

O segundo comando criou ni.dll (e .obj e .lib), que sabe como encontrar as funções necessárias do spam e também do executável do Python.

Nem todo identificador é exportado para a tabela de pesquisa. Se você deseja que outros módulos (incluindo Python) possam ver seus identificadores, é necessário dizer _declspec(dllexport), como em void _declspec(dllexport) initspam(void) ou PyObject _declspec(dllexport) *NiGetSpamData(void).

O Developer Studio incluirá muitas bibliotecas importadas que você realmente não precisa, adicionando cerca de 100K ao seu executável. Para se livrar delas, use a caixa de diálogo de configurações do projeto, na aba vincular, para especificar ignorar bibliotecas padrão. Adicione o msvcrtxx.lib correto à lista de bibliotecas.