Archivo de la categoría: Lenguaje C

Instalar ZeroC Ice para Python en CentOS 5.7

ZeroC Ice logo
ZeroC Ice logo

Ice o  Internet Communications Engine  es una plataforma middleware orientada a objetos, que permite escribir aplicaciones distribuidas con muy poco coste. Provee herramientas, APIs, bibliotecas y bindings para la construcciones de aplicaciones cliente-servidor de forma escalable.

Es una buena herramienta de trabajo para entornos que sean bastante heterogéneos, donde el cliente y servidor puede estar escritos en lenguajes de programación muy diferentes y ejecutarse sobre plataformas, sistemas operativos o arquitecturas muy variadas.

Su fuerte radica en un gran soporte de tecnologías de red con interacciones a bajo nivel de red, enfoque en la lógica de las aplicaciones y portabilidad de entornos muy diversos.

Otro punto fuerte a destacar es la abstracción de red para detalles como abrir conexiones de red, serializado y des-serializado de datos transmitidos por red, reintentos fallidos de conexión y muchos otros detalles más de bajo nivel que ayudan a simplificar la capa de red.

Soporta bindings con bastantes lenguajes de programación como: 
C++.NETJava,PythonObjective-CRubyPHP, y ActionScript.

Debido a estas características Ice es usado por muchas compañias con misiones críticas en el mundo. La curva de aprendizaje es sencilla y existe una gran infraestructura de aplicaciones técnicas que se demandan.

Ice es software libre bajo licencia GPLv2 y comercializado bajo la marca ZeroC.

Instalación con repositorio

Esta opción de instalación es sencilla y cómoda, pero no esta aconsejada para desarrolladores que necesiten actualizaciones con bastante frecuencia o disponer de una versión determinada.

Descargar el repo (en estos momentos de escribir el artículo para la versión 3.4) desde la página web oficial:

 # wget http://download.zeroc.com/Ice/3.4/rhel6/zeroc-ice-rhel6.repo -O /etc/yum.repos.d/zeroc-ice-rhel6.repo

Activar el repositorio e instalar ZeroC Ice y biblioteca Ice para Python:

 # yum --enablerepo zeroc-ice install -y ice ice-python

Instalación desde fuentes

Si tu necesidad esta más orientada al enfoque desarrollador, puedes compilar la versión que requieras o en desarrollo. En el momento de escribir este artículo se compila para la versión 3.4.2 de Ice.

Instalar portable C preprocessor desde el repo de ZeroC Ice (también puedes optar por bajar y compilar los fuentes de mcpp, pero no es necesario):

# yum install -y mcpp-devel

Después bajamos los fuentes de Ice, descomprimimos y compilamos la versión en C++ y su binding para Python (en este caso para la versión 3.4.2 de Ice). Puedes hacerlo todo en una línea con:

# wget http://zeroc.com/download/Ice/3.4/Ice-3.4.2.tar.gz; tar xvzf Ice-*.tar.gz; cd Ice-*/cpp; make; make install; cd ../py/; make;make install

La instalación de Ice quedará bajo /opt/Ice-3.4.2/ y el binding python sobre /opt/Ice-3.4.2/python. Es importante recalcar que el binding python se asociara con la versión de defecto de Python en el sistema si dispones de varias.

Por último, es necesario indicar el el PATH y el PYTHONPATH del sistema, donde se encuentra la instalación de Ice y su binding de Python:

# export PATH=/opt/Ice-3.4.2/bin:$PATH
# export PYTHONPATH=/opt/Ice-3.4.2/python:$PYTHONPATH

 

Como usar bibliotecas de C en Python

Para determinadas aplicaciones, la eficiencia y el rendimiento es clave. Python es un buen lenguaje para programar rápido y mucho en pocas líneas, pero a veces queremos bajar más de nivel para ofrecer mejores prestaciones.

Pero esta decisión no debe implicar, perder nuestra comodidad que tenemos con Python. Existe una biblioteca incluida nativamente desde Python 2.5 llamada ctypes que nos permite utilizar funciones y bibliotecas compartidas de C (en cualquier sistema operativo) obteniendo la eficiencia del lenguaje C pero sin renunciar a las bondades de sintaxis de Python.

Ejemplo práctico y sencillo

Veamos un ejemplo sencillo para ilustrar esta gran funcionalidad. Para empezar podemos crear un simple archivo de código fuente en C y por ejemplo llamarlo ‘libtest.c’.

Por ejemplo, en esta biblioteca C, haremos una simple función que multiplique enteros:

int multiply(int num1, int num2)
{
    return num1 * num2;
}

Ahora necesitaremos compilar nuestra biblioteca ‘libtest.c’ en una biblioteca válida, para ello utilizaremos los siguientes comandos de compilación:

gcc -c -fPIC libtest.c
gcc -shared libtest.o -o libtest.so

Ello creara una versión final de la biblioteca para GNU/Linux llamada libtest.so que podamos usar en Python.

Ahora probemos nuestra biblioteca (debes poner el path o camino de libtest.so en el código para que funcione o incluirlo en las rutas donde GNU/Linux buscara las bibliotecas comunes):

# A partir de Python >= 2.5 se incluye la libreria *ctypes* en el core, comprobar si esta disponible
import sys
try:
    from ctypes import *
except ImportError:
    print 'ERROR! La biblioteca *ctypes* para Python no esta disponible.'
    sys.exit(-1)

libtest = cdll.LoadLibrary('ruta_a_la_biblioteca/libtest.so')
print 'La multiplicación de 2 * 2 es:', libtest.multiply(2, 2)

Este ejemplo imprimirá 4 si todo ha ido correctamente. Como se aprecia, no es nada complicado usar código C nativo en Python.

Ejemplo con parámetros

Podemos crear ejemplos más complejos, incluso con funciones que tengan argumentos como punteros o pasados por referencia. Por ejemplo una lista donde se sumen todos los valores, su media:

/* suma de una lista de valores */
double sum(double *data, int n)
{
    int i=0;
    double sum=0.0;

    for (i=0; i < n; i++)
        sum += data[i];
    return sum;
}

/* media de una lista de valores */
double mean(double *data, int n)
{
    double s = sum(data, n);
    return s/((double)n);
}

Lo compilamos de nuevo con:t:

$ gcc -shared -fPIC liblist.c -o liblist.so

Y en python de nuevo usamos ctypes:

from ctypes import *
so = CDLL("liblist.so")

# Establecemos las interfaces con los argumentos

# y los valores que devuelven
so.mean.argtypes= [POINTER(c_double), c_int]
so.mean.restype = c_double
so.sum.argtypes = [POINTER(c_double), c_int]
so.sum.restype = c_double

# Llamamos a las funciones
def cmean(self, dlist):
    doubleArray = c_double*len(dlist) # Tipo de dato (double[n])
    cx = doubleArray(*dlist) # Array actual
    n = len(cx) # Longitud del dato

    result = so.mean(cx, n)
    return float(result)

def csum(self, dlist):
    doubleArray = c_double*len(dlist)
    cx = doubleArray(*dlist)
    n = len(cx)

    result = so.sum(cx, n)
    return float(result)

# Ahora podemos usar estas funciones como si fuera python puro!
data = [1,2,3,4,5,6]
print cmean(data)
print csum(data)

Con ctypes podemos crear un wrapper (envoltura o adaptador) para una biblioteca matemática u otras funciones que nos interesen.

Ejemplo con MPI

Otro ejemplo práctico sería poder escribir MPI en python desde C, como el ejemplo que escribí hace unos días. Notad que ya existen implementaciones python de MPI, pero nunca esta demás saber como funcionan y que principios siguen, por ejemplo para iniciarse en openMPI desde python, podríamos hacer:

#!/usr/bin/python

import sys

try:
from ctypes import CDLL, pythonapi, c_int, POINTER, c_char_p, byref, RTLD_GLOBAL
from ctypes.util import find_library
except ImportError:
print 'ERROR! La biblioteca *ctypes* para Python no esta disponible!'
sys.exit(-1)

libc = CDLL('libc.so.6')

print libc._handle, libc._name

f = pythonapi.Py_GetArgcArgv
argc = c_int()
argv = POINTER(c_char_p)()
f(byref(argc), byref(argv))

mpi = CDLL(find_library('mpi'), RTLD_GLOBAL)
print mpi._handle, mpi._name

libc.printf("Hola mundo");

mpi.MPI_Finalize()

En este ejemplo, utilizo el método find_library() de ctypes que me permite olvidarme de localizar la ruta de una biblioteca y así buscar por si mismo el programa su ruta. Se importa la biblioteca de C y la de MPI y se hace un simple hola mundo mostrando algunos datos sobre las bibliotecas también. Para ctypes únicamente he importando las funciones necesarias, evitando que se cargue toda la biblioteca.

En definitiva ctypes es una potente herramienta que todo desarrollador Python debería tener en cuenta cuando quiera realizar desarrollos más eficientes y aprovechar implementaciones de otras bibliotecas compartidas.

Palíndromo en C desde un fichero

Introducción

Estos días he tenido que hacer un pequeño ejemplo de un programa en C, aparentemente sencillo, pero del que no he encontrado buenas soluciones en Google, ni siguiera en otros idiomas más dados.

Casi todos los ejemplos que veía por internet eran poco elegantes, no cuidaban el tratamiento de errores y además todos pedían la entrada de datos por línea de órdenes. Asi que me ha parecido útil ponerlo en el blog, para que gente que se encuentre con el problema en un futuro, tenga una buena referencia o al menos aproximada a su solución.

Descripción del problema

El problema consiste en crear un programa en C que pida el nombre de un archivo de texto y devuelva el número y el palíndromo de todos los posibles palíndromos encontrados en dicho archivo.

Un palíndromo segun Wikipedia es:

Una palabra, número o frase que se lee igual hacia adelante que hacia atrás.

Por ejemplo algunas palabras: ala, bob, solos, reconocer, …

Y alguna frases como:

Dabale arroz a la zorra el abad

En nuestro caso por simplicidad y caso común detectaremos solo las palabras y no frases o textos enteros, ya que si no esto podría complicarse hasta un nivel mayor, aunque tampoco sería demasiado complicado.

Resolución del problema

Supongamos que tenemos un fichero de ejemplo con algunos palíndromos como el siguiente:

Este es un archivo de prueba
que contiene palíndromos como
pueden ser: ala anilina reconocer solos otto bob
pero por ejemplo no son palindromos
solas ola

El siguiente programa, detectara los palíndromos según nuestros propósitos (lo explico más detalladamente después):

#include < stdio.h>;
#include < stdlib.h>;
#include < string.h>;

int searchPalin(char *cad)
{
    unsigned int i = 0, d = (int) strlen(cad)-1;
    int palin=1;
    while ((palin == 1) && (i < ((int)strlen(cad))))
    {
        if (cad[i] != cad[d]) palin=0;
        else
        {
            i++;
            d--;
        }
    }
    return palin;
}

int main(int argc, char** argv)
{
    if(argc < 2)
    {
        fprintf(stderr,"palin: opción inválida\nEl primer argumento debe ser un nombre de archivo.\n");
        return 1;
    }

    char *namefile=argv[1];
    FILE *fd;

    if((fd=fopen(namefile,"r")) == NULL)
    {
        fprintf(stderr,"palin: Error al abrir el archivo.\n");
        return 1;
    }
    else
    {
        printf("Buscando palíndromos en archivo: %s\n",namefile);

        fseek(fd, 0, SEEK_END);
        long length = ftell(fd);
        fseek(fd, 0, SEEK_SET);
        char *w;
        w = (char *) malloc(length*sizeof(char *));
        int i,j,p=0;

        fread(w, sizeof(unsigned int), length, fd);
        fclose(fd);

        char *aux;
        aux = (char *) malloc(strlen(w)*sizeof(char *));
        for(i=0,j=0;i

Bien pasemos a la explicación detallada:

Las tres primeras lineas incluyen en las bibliotecas necesarias para el funcionamiento del programa, la de entrada/salida estandar, la biblioteca estandar y la biblioteca de cadenas (útil para estos propósitos).

La función searchPalin es la que puedes encontrar en la mayoría de los sitios de internet, se encarga de contar los caracteres de la cadena e ir viendo desde el principio al final si van coincidiendo por el principio y por el final. Si es así retorna un valor booleano verdadero (líneas 5 a 19).

En la función main primero comprobamos que hemos recibido un número correcto de argumentos, al menos los necesarios, ya que más de los estrictos, serán ignorados (líneas 23 a 27).

Como segunda comprobación tenemos que asegurarnos que el parámetro del nombre de fichero dado, existe y se puede abrir para lectura, si es así ya podemos procesar el archivo en busca de palíndromos (líneas 29 a 36).

Por comodidad para realizar la lectura del archivo utilizamos la función fread() (línea 48), por lo que será necesario saber la longitud del fichero. La longitud de un fichero no es difícil de calcular, pero hay que tener un aspecto en cuenta.

La función que tenemos disponible para el cálculo de la longitud de un fichero es ftell(), pero dicha función retorna el indicador de posición de fichero, que al comenzar la lectura será 0, luego, necesitamos posicionarnos al final del fichero, después llamar a ftell() y por último volver al principio del fichero para empezar a leer caracteres mediante fread(). Para posicionarnos al principio y al final utilizamos fseek() consiguiendo nuestro propósito (lineas 41 a 43) .

Después cerraremos el archivo con fclose() (línea 49), ya que habremos leído todos los caracteres y los habremos almacenado en una variable llamada w, con la peculiaridad, de que se hace una reserva dinámica de memoria con la cantidad exacta de caracteres del archivo, aprovechando que ya habíamos calculado la longitud del archivo (línea 44 y45), por lo que no nos limitamos a una cantidad en un archivo (como hacen otras posibles implementaciones).

También necesitaremos una cadena auxiliar (aux) en la que reservaremos la cantidad de carácteres que tenga w (línea 51 y 52).

Posteriormente procesaremos nuestra cadena y si leemos un carácter normal que no sea un espacio en blanco o un tabulador, lo almacenaremos en nuestra cadena auxiliar (líneas 61 a 68). Si por el contrario encontramos un salto de línea o un espacio en blanco (lineas 55 a 60) , entonces sera una palabra distinta, por lo que procesaremos el contenido que tengamos en aux, ademas de añadir el carácter '\0' de final de cadena (línea 57). Esto último es importante, ya que si no añadimos ese carácter no se interpretara bien la cadena al no estar completa y ser una sucesión de caracteres (peculiaridades de C).

Llamaremos a la función searchPalin para que nos verifique si la cadena aux es un palindromo y si es, mostraremos el número de palindromo y el palíndromo en sí.

Compilación y ejecución

Para compilar el archivo, se puede hacer a pelo, o bien podemos utilizar un makefile (mucho más cómodo) como el siguiente:

BINARY = palin
CFLAGS = -o
CC = gcc
RM = rm

all:
    @echo Compilando...
 $(CC) $(BINARY).c $(CFLAGS) $(BINARY)
   @echo Hecho.

clean:
  @echo Limpiando...
  $(RM) -f *.o *~
 @echo Hecho.

Para compilar escribimos:

$ make

Para ejecutar el binario con el fichero de ejemplo.txt:

$ ./palin ejemplo.txt

Y debe dar una salida como:

Buscando palíndromos en archivo: ejemplo_old.txt
1. ala
2. anilina
3. reconocer
4. solos
5. otto
6. bob

Para limpiar la compilación:

$ make clean

Y eso es todo, no es muy complicado, pero requiere varios detalles en los que se debe prestar atención.

Cualquier sugerencia de mejora o comentario adicional es bienvenido.

Happy coding!