A biblioteca ctypes do Python permite a chamada de funções em bibliotecas compartilhadas (DLLs no Windows ou arquivos .so no Linux) escritas em C ou C++. Para garantir a interoperabilidade, é necessário mapear corretamente os tipos de dados entre as linguagens.
Mapeamento de Tipos: ctypes vs C
| Tipo ctypes | Tipo C | Tipo Python |
|---|---|---|
| c_bool | _Bool | bool |
| c_char | char | string (1 caractere) |
| c_byte | char | int |
| c_ubyte | unsigned char | int |
| c_short | short | int |
| c_int | int | int |
| c_uint | unsigned int | int |
| c_long | long | int |
| c_float | float | float |
| c_double | double | float |
| c_char_p | char * | string ou None |
| c_void_p | void * | int ou None |
Implementação em C++
Para que as funções C++ sejam acessíveis via ctypes, elas devem ser expostas usando extern "C" para evitar a alteração de nomes (name mangling) pelo compilador C++.
#include <iostream>
#include <vector>
// Arquivo: biblioteca.cpp
// Compilação Linux: g++ -o biblioteca.so -shared -fPIC biblioteca.cpp
// Compilação Windows: g++ -o biblioteca.dll -shared biblioteca.cpp
extern "C" {
int processar_dados(int valor_int, char letra, const char* texto, int vetor_int[4], char matriz_char[2][2]) {
std::cout << "Integro recebido: " << valor_int << std::endl;
std::cout << "Caractere recebido: " << letra << std::endl;
std::cout << "String recebida: " << texto << std::endl;
std::cout << "Array unidimensional: ";
for(int i = 0; i < 4; i++) {
std::cout << vetor_int[i] << " ";
}
std::cout << std::endl;
std::cout << "Matriz bidimensional:" << std::endl;
for(int i = 0; i < 2; i++) {
for(int j = 0; j < 2; j++) {
std::cout << "[" << matriz_char[i][j] << "] ";
}
std::cout << std::endl;
}
return 1;
}
}
Implementação em Python
No lado do Python, utilizamos a classe CDLL para carrgear a biblioteca e definimos as estruturas de dados necessárias para matrizes e ponteiros.
import ctypes
import os
# Carregar a biblioteca compartilhada
lib_path = os.path.abspath("biblioteca.so")
lib = ctypes.CDLL(lib_path)
# Preparação de strings (UTF-8 para compatibilidade)
string_input = "Texto de exemplo".encode('utf-8')
# Definição e inicialização de um array unidimensional (4 inteiros)
TipoArrayInt4 = ctypes.c_int * 4
meu_vetor = TipoArrayInt4(10, 20, 30, 40)
# Definição e inicialização de uma matriz 2x2 de caracteres
TipoLinhaChar2 = ctypes.c_char * 2
TipoMatrizChar2x2 = TipoLinhaChar2 * 2
minha_matriz = TipoMatrizChar2x2()
minha_matriz[0][0] = b'W'
minha_matriz[0][1] = b'X'
minha_matriz[1][0] = b'Y'
minha_matriz[1][1] = b'Z'
# Chamada da função C++
# Protótipo: int processar_dados(int, char, char*, int[4], char[2][2])
resultado = lib.processar_dados(
ctypes.c_int(500),
ctypes.c_char(b'R'),
ctypes.c_char_p(string_input),
meu_vetor,
minha_matriz
)
print(f"Execução finalizada com status: {resultado}")
Considerações sobre Matrizes Multidimensionais
Ao lidar com matrizes multidimensionais no ctypes, a estrutura é criada multilpicando o tipo base pelo número de colunas e, em seguida, multiplicando o resultado pelo número de linhas. Isso garante que a disposição na memória seja contígua, exatamente como esperado por um compilador C/C++.