Sunday, February 28, 2010

Miniprueba con SWIG

Hoy hice una pequeña prueba con SWIG.
El ejemplo que aparece en SWIG: donde C y Python se dan la mano (está muuuy bien explicado) hizo que me picará la curiosidad.

SWIG es una herramienta que permite conectar programas escritos en C/C++ con diferentes lenguajes de alto nivel (lenguajes que soporta).
En este caso, lo probé con Python y C.

Este es el header de mi código en C:
#ifndef __DATOS_PERSONALES_H__
#define  __DATOS_PERSONALES_H__

#include 
#include 

/* Tipos */
typedef struct {
  char nombre[150];
  int  edad;
}DatosPersona;

/* Prototipos */
extern int edad_get(DatosPersona a);
extern char * nombre_get(DatosPersona a);
extern void nombre_set(DatosPersona *a, char * nombre);
extern void edad_set(DatosPersona *a, int edad);
extern void muestra(DatosPersona a);

#endif /* __DATOS_PERSONALES_H__ */

Y este es el código en si:
#include "datosPersonales.h"

int edad_get(DatosPersona a)
{
    return a.edad;
}

char * nombre_get(DatosPersona a)
{
   return a.nombre;
}

void nombre_set(DatosPersona *a, char * nombre)
{
   strcpy(a->nombre, nombre);
}

void edad_set(DatosPersona *a, int edad)
{
    a->edad = edad;
}

void muestra(DatosPersona a)
{
    printf("Me llamo %s y tengo %d años.\n", a.nombre, a.edad);
}

Ahora viene lo interesante. Hay que definir una interfaz en un fichero con extensión .i, en la que se dan a SWIG instrucciones para construir el envoltorio para usar el código.
Hay muchas posibilidades, (la documentación es muy extensa), y después de pasarme unas horillas buscando llegué a lo siguiente:
%module datosPersonales
%{
#include "datosPersonales.h"
%}

typedef struct {
  char nombre[150];
  int  edad;
}DatosPersona;

%extend DatosPersona
{
    DatosPersona(char *nombre, int edad)
    {
        DatosPersona *datos = (DatosPersona *) malloc(sizeof(DatosPersona));
        strcpy ( datos->nombre, nombre );
        
        datos->edad = edad;
        return datos;
    }
    
    ~DatosPersona() 
    { 
        free(self); 
    }
    
    int getEdad()
    { 
        edad_get( *self ); 
    }
    
    char * getNombre()
    { 
        return nombre_get( *self ); 
    }
    
    void setNombre(char *nombre)
    {
        nombre_set(self, nombre);
    }
    
    void setEdad(int edad)
    {
        edad_set(self, edad);
    }
    
    void muestraDatos()
    {
        muestra(*self);
    } 
};

En esta interfaz se extiende el tipo de datos de C con una serie de funciones miembro que nos permitirán usar orientación a objetos en Python.

Para compilar hice lo siguiente:
$ swig -python datosPersonales.i
$ ls
datosPersonales.c  datosPersonales.i   datosPersonales_wrap.c  
datosPersonales.h  datosPersonales.py
$ gcc -c datosPersonales.c
$ ls
datosPersonales.c  datosPersonales.i  datosPersonales.py
datosPersonales.h  datosPersonales.o  datosPersonales_wrap.c
$ gcc -c datosPersonales_wrap.c -I /usr/include/python2.5/
$ ls
datosPersonales.c  datosPersonales.i  datosPersonales.py datosPersonales_wrap.o 
datosPersonales.h  datosPersonales.o  datosPersonales_wrap.c
$ gcc -shared datosPersonales.o datosPersonales_wrap.o -o _datosPersonales.so
$ ls
datosPersonales.c  datosPersonales.i  datosPersonales.py   datosPersonales_wrap.c  
datosPersonales.h  datosPersonales.o  _datosPersonales.so  datosPersonales_wrap.o

Ahora ya está todo listo para poder usar el módulo datosPersonales.py desde Python.

import datosPersonales as dp

datos = dp.DatosPersona("Lolo", 34)

print datos.getEdad()
print datos.getNombre()

datos.muestraDatos()

datos.setNombre("Javi")
datos.setEdad(31)
datos.muestraDatos()

del datos

Y esto es lo que resulta al ejecutar el script:

$ python runMe.py 
34
Lolo
Me llamo Lolo y tengo 34 años.
Me llamo Javi y tengo 31 años.

Este ejemplo no deja de ser una tontería, pero las posibilidades de SWIG son grandísimas (sólo hay que ver algunos de los artículos online que aparecen en Internet).
Más adelante, si tengo algo de tiempo, me gustaría correr alguna simulación escrita en C desde Python y usar MatPlotLib para hacer los gráficos.

No comments:

Post a Comment