Introducción
ctypes es una biblioteca de funciones externas incluida en Python. Proporciona tipos de datos compatibles con C y permite invocar funciones desde bibliotecas dinámicas (DLL) o bibliotecas compartidas compiladas en C o C++. Este módulo facilita el desarrrollo de evnolturas para estas bibliotecas directamente en Python.
El código presentado se ejecuta en un sistema Windows con Python 64-bit versión 3.9.12. Para su uso en Linux, consultar entradas anteriores del blog.
1. Estructura básica del código
1.1 Crear archivo header.h
#pragma once
#define ExportDll __declspec(dllexport)
typedef struct {
int coordenada_x;
double coordenada_y;
} Punto;
extern "C"
{
ExportDll void saludo_mundial(void);
ExportDll char mostrar_caracter(char caracter);
ExportDll double suma_valores(int valor1, float valor2, double valor3);
ExportDll char* prueba_cadena(char *cadena);
ExportDll int procesar_arreglos(int arreglo1[], int tamano1, int arreglo2[][3], int tamano2);
ExportDll double* calcular_resultados(int *ptr1, float *ptr2, double *ptr3);
ExportDll void desplegar_coordenada(Punto punto);
}
1.2 Crear archivo implementacion.cpp
#include <iostream>
#include <cstring>
#include "header.h"
void saludo_mundial() {
std::cout << "Saludo Mundial!" << std::endl;
}
char mostrar_caracter(char caracter) {
std::cout << caracter << std::endl;
return caracter;
}
double suma_valores(int valor1, float valor2, double valor3) {
return valor1 + valor2 + valor3;
}
char* prueba_cadena(char *cadena) {
static char mensaje[] = "saludo";
char *puntero;
int indice;
puntero = mensaje;
for (indice = 0; indice < 2; indice++) {
*(cadena + indice) = mensaje[indice];
}
return puntero;
}
double* calcular_resultados(int *ptr1, float *ptr2, double *ptr3) {
static double resultados[5] = {2000.0, 4.0, 6.8, 34.0, 100.0};
double *puntero_resultado;
int i;
resultados[0] = resultados[0] + *ptr1 + *ptr2 + *ptr3;
puntero_resultado = resultados;
return puntero_resultado;
}
int procesar_arreglos(int arreglo1[], int tamano1, int arreglo2[][3], int tamano2) {
int i;
for (i = 0; i < tamano1; i++) {
arreglo1[i] = arreglo1[i] + 20;
}
int fila, columna;
for (fila = 0; fila < tamano2; fila++) {
for (columna = 0; columna < 3; columna++) {
arreglo2[fila][columna] = arreglo2[fila][columna] + 200;
}
}
return 0;
}
void desplegar_coordenada(Punto punto) {
std::cout << "X: " << punto.coordenada_x << ", Y: " << punto.coordenada_y << std::endl;
}
</cstring></iostream>
1.3 Compilar código C++
g++ implementacion.cpp -fPIC -static -shared -o biblioteca.dll
1.4 Crear archivo python_interfaz.py
# -*- coding: UTF-8 -*-
from ctypes import *
# Cargar la biblioteca dinámica
lib = CDLL('./biblioteca.dll')
# Ejecutar función simple
lib.saludo_mundial()
# Configurar tipos para función que recibe y devuelve char
lib.mostrar_caracter.restype = c_char
resultado_caracter = lib.mostrar_caracter(c_char(b'X'))
print("Resultado caracter: ", resultado_caracter)
# Configurar tipos para operación matemática compleja
lib.suma_valores.restype = c_double
lib.suma_valores.argtypes = [c_int, c_float, c_double]
resultado_decimal = lib.suma_valores(c_int(10), c_float(1.5), c_double(0.1))
print("Resultado decimal: ", resultado_decimal)
# Manejo de cadenas de caracteres
lib.prueba_cadena.restype = c_char_p
lib.prueba_cadena.argtypes = [c_char_p]
buffer_cadena = c_char_p(b"t")
resultado = lib.prueba_cadena(buffer_cadena)
print(resultado)
print(buffer_cadena.value)
# Operaciones con punteros
lib.calcular_resultados.restype = POINTER(c_double)
lib.calcular_resultados.argtypes = [POINTER(c_int), POINTER(c_float), POINTER(c_double)]
resultados = lib.calcular_resultados(pointer(c_int(200)), pointer(c_float(0.2)), pointer(c_double(0.02)))
for idx in range(5):
print(resultados[idx])
# Uso de arreglos unidimensionales y bidimensionales
lib.procesar_arreglos.argtypes = [c_int * 4, c_int, (c_int * 3)*4, c_int]
arreglo_uno = (c_int * 4)(2, 4, 6, 8)
arreglo_dos = ((c_int * 3)*4)((2, 4, 6), (8, 10, 12), (14, 16, 18), (20, 22, 24))
lib.procesar_arreglos(arreglo_uno, 4, arreglo_dos, 4)
for idx in range(4):
print("Python arreglo 1:", arreglo_uno[idx])
for idx in range(4):
for jdx in range(3):
print("Python arreglo 2:", arreglo_dos[idx][jdx])
# Segunda forma usando punteros
lib.procesar_arreglos.argtypes = [POINTER(c_int), c_int, POINTER(c_int), c_int]
arreglo_uno = (c_int * 4)(2, 4, 6, 8)
arreglo_uno_ptr = cast(arreglo_uno, POINTER(c_int))
arreglo_dos = ((c_int * 3)*4)((2, 4, 6), (8, 10, 12), (14, 16, 18), (20, 22, 24))
arreglo_dos_ptr = cast(arreglo_dos, POINTER(c_int))
lib.procesar_arreglos(arreglo_uno_ptr, 4, arreglo_dos_ptr, 4)
for idx in range(4):
print("Python arreglo 1 (puntero):", arreglo_uno_ptr[idx])
for idx in range(12):
print("Python arreglo 2 (puntero):", arreglo_dos_ptr[idx])
# Definición y uso de estructuras
class Punto(Structure):
_fields_ = [
('x', c_int),
('y', c_double),
]
coordenada = Punto(5, 10)
print(coordenada.x, coordenada.y)
lib.desplegar_coordenada(coordenada)
print(coordenada.x, coordenada.y)
1.5 Ejecutar código Python
python python_interfaz.py