En los títulos y los textos vais a encontrar unas cuantas citaciones cinematográficas (y si, soy un cinéfilo). Si no os interesan podéis fingir no verlas, ya que no son fundamentales para la comprensión de los post...

Este blog es la versión en Español de mi blog en Italiano L'arte della programmazione in C. Espero que mis traducciones sean comprensibles...

viernes, 13 de mayo de 2016

Strerror Simple
cómo escribir una pseudo-strerror en C

Este es un post fácil. Tal vez un poco más de el Blood del debut de los grandes Coen brothers (um... la sangre nunca es fácil). Sí, un post fácil fácil, pero más útil de lo que parece, sólo para demostrar que a problema simple solución simple: si para hacer una tontería habéis escrito 1000000 de líneas incomprensibles de código os habéis equivocado en algo: mejor borrar y empezar de nuevo, sin duda os saldrá mejor.

...¿y esto le llaman simple? ¿¿¿SIMPLE???
Entonces, supongamos de tener unos códigos que representan algo (errores, tipos de mensajes, warning de sistema, zapatillas, quesos... lo que sea), y queréis, por ejemplo, mostrarlos en un bonito sistema de log (tal vez como el que se ha visto aquí, aquí y aquí), pero no como números (bueno, somos seres humanos, no Sheldon Cooper), estamos acostumbrados a ver cosas, no números. Queremos ver las cadenas mnemónicas correspondientes a los números.
...y desde hoy os voy a hablar solamente de forma binaria...
Vale, escribir el código para una cosa así es realmente muy simple... ¡Vamos con el codigo!
#include<stdio.h>

// códigos cadenas
#define CODE_SPIDERMAN  0
#define CODE_THOR       1
#define CODE_DAREDEVIL  2
#define CODE_PUNISHER   3

// array cadenas
static const char* const strings[] = {
    "Spiderman",
    "Thor",
    "Daredevil",
    "Punisher"
};

// obtiene una cadena
const char* getString(
    int code)
{
    // calcula size
    int size = sizeof(strings) / sizeof(char*);

    // test si el codigo está en overflow
    if (code >= 0 && code < size)
        return(strings[code]);
    else
        return("error: código no valido");
}

// main() para test
void main()
{
    // loop de input
    for (;;) {
        // pide el codigo
        int code;
        printf("get cadena numero? ");
        scanf("%d", &code);

        // enseña la cadena correspondiente al codigo
        printf("cadena = %s\n", getString(code));
    }
}
Entonces, a fin de no repetirme: el código anterior es, por supuesto,  auto-explicativo, ampliamente comentado y los comentarios hablan por sí mismos. 


Como os habréis dado cuenta es suficiente (por lo menos en una implementación básica) definir los códigos y escribir las cadenas correspondientes (¡en el mismo orden!) en un array. A continuación, se escribe la función para extraerlas que ejecuta, simplemente, un return de array[codigo]. He añadido un mínimo (esencial) de controles, para evitar el uso de códigos negativos o no existentes (overflow). Notar que el size se calcula de forma dinámica, por lo que podemos añadir, con el tiempo, códigos (y cadenas), sin necesidad de cambiar la getString() (¡muy bien!). Obviamente el código presentado (que también incluye un sencillo main() de test interactivo) se puede complicar y sofisticar como se desee, pero respetando la idea básica, que es simple.

En un proyecto real, el file anterior hay que dividirlo en tres: los códigos (y un prototipo) irán a un header-file que será incluido por todos los source-files de la aplicación que necesitan utilizar la getString(). La getString() y el array (estático) de cadenas irá en un source-file específico (que llamaremos getstring.c) que habrá que compilar y linkar con el resto de los archivos del proyecto.

¿Porque en el título he mencionado la strerror()? Porque la nuestra getString() hace, más o menos, el mismo trabajo que la strerror() de sistema, o sea devuelve la cadena correspondiente al código de error errno. Obviamente, la strerror() es un poco más sofisticada: utiliza un buffer estático interno sobre el cual ejecuta strcpy(), y otras cositas... y es por causa de el buffer interno que no es thread safe y, en algunos casos, nos obliga a utilizar la strerror_r(): pero eso es otra historia, y esto es un post simple, no hay que divagar.

Por cierto, si alguien de trabajo hace el que complica la cosas sencillas (que, por desgracia, es un trabajo bastante común) todo lo dicho hasta ahora le resultará indiferente, tal vez incluso molesto. Pero tal vez no debería leer este blog, será pasado por aquí por error.

¡Hasta el próximo post!