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...

miércoles, 16 de octubre de 2013

La union hace la fuerza
cómo usar las union en C

Que levante la mano quien ha escrito recientemente código que utiliza las union. O bien, levante la mano, sin antes ir a volver a leer un manual de C, aquellos de vosotros que saben cómo usar y/o describir perfectamente las union ? Uhmm ... Veo pocas manos levantadas. El hecho es que la union es una de las partes del C menos conocida, de uso dudoso y poco frecuente, en definitiva, una de las partes que, por falta de práctica, se olvida.

Para introducir el tema (y , repito, sin consultar antes un manual de C, de lo contrario no os váis a poner a prueba), os voy a contar una historia real. Un tipo que conozco, cuyas iniciales son A.A. (no puedo decir más por privacidad) fue, hace mucho tiempo, a una entrevista de trabajo, y le propusieron una  simple test como este (más o menos):

#include <stdio.h>

void main(void)
{
    union u_test {
        int  i;
        char s[4];
    } my_test;

    my_test.i = 0x01020304;

    // que imprime la linea siguiente?
    printf("s[0]=%d s[1]=%d s[2]=%d s[3]=%d\n",
        my_test.s[0], my_test.s[1], my_test.s[2], my_test.s[3]);
}
En este caso, por las razones mencionadas al principio del post (uso dudoso y poco frecuente), nuestro amigo que no utilizaba una unión por lo menos desde hace 15 años (sí, 15 años !), se sorprendió al principio, luego forzó la memoria y no cae en el primer nivel de error que induce este test un poco engañoso, primero nivel de error que te llevaría a decir:

He inicializado el campo i y voy a leer el campo s: entonces la printf() imprime valores no definidos!

No, él se recordó que "las union asignan memoria para la mayor de las variables internas, variables que comparten el mismo espacio de memoria", y no cayó en la trampa, y respondió casi a la perfección... casi, porque cae el segundo nivel de error, el que te hace decir:

He inicializado i, pero leo s, que es como si hubiera sido inicializado, entonces la respuesta es : s[0]=1 s[1]=2 s[2]=3 s[3]=4 !

Ok, casi justo: de hecho, la respuesta es:

s[0]=4 s[1]=3 s[2]=2 s[3]=1

Nuestro amigo A.A. se dio cuenta de eso volviendo a casa, cuando ya se había hecho el error: en la ubicación s[0] termina la parte menos significativa de i (entonces 0x04), y así sucesivamente con los otros números, por lo que el resultado fue en orden inverso. Bueno, cosas que pasan...

Si buscáis en la red con google váis a encontrar muchos útiles ejemplos de uso de la union, entre los cuales, yo diría, los más interesantes son los para el tratamiento de datos con contenido variable (protocolos de comunicación, por ejemplo) con los cuales se quiere tratar, con la misma estructura de datos, información de diferente tipo, y interpretando la union, cada vez, en la forma correcta mediante la selección de un oportuno flag mantenido por el programa. Esto permite hacer programas flexibles y económicos en términos de memoria (en comparación con el uso de las struct), que en algunos entornos de desarrollo podrían ser útiles.

Ojo, sin embargo: por el modo mismo en que funcionan las union son flexibles pero engañosas, y los errores imprevistos de funcionamiento están detrás de la esquina esperando. Hay que ser muy estricto en el mantener, y testear, el flag de estado que nos indica cual personalidad de la union estamos utilizando en cada momento.

Hasta el próximo post.

Este post está inspirado en una historia real, pero cualquier parecido con hechos o personas reales es mera coincidencia. (uhmm ???)

sábado, 18 de mayo de 2013

La maldición de la Callback de jade II – El retorno
cómo escribir una Callback en C - pt.2

He decidido hacer una pausa en el tema del C a objetos: ya he escrito dos post sobre el tema y no quiero aburrirme ni aburriros. Lo sé, se podría profundizar más detalles y hablar (como prometido) de C vs C + +, pero, en este momento, no me da la gana. Sin embargo, tarde o temprano, volveremos seguro en eso...

Así que, hoy, volvemos a un tema ya tratado aquí, y sobre el cual (me dí cuenta ayer) se podría añadir algo interesante. Estoy hablando, otra vez, de las funciones callback.

Por supuesto, antes de seguir leyendo, debéis refrescaros la memoria releyendo el otro post sobre el tema, ya que es una extensión de esto, y están estrechamente vinculados.

Pausa para la reflexión...

¿Ya estáis de vuelta? ¿pero cómo? Todavía no habéis releído el antiguo post? Hay que leerlo, gracias...

Otra pausa para la reflexión...

Bueno, ahora podemos continuar.

Así que, cómo habréis re-notado, el ejemplo que yo había propuesto era, yo diría, clásico, inspirado en el uso de qsort(), entonces con una callback que se llama sin argumentos, pero que, en realidad, necesita dos que se generaran internamente por la misma función (la mysort() del ejemplo, o la qsort(), si prefereis).

Entonces, resumamos el flujo del ejemplo: he escrito una función main() que utiliza mysort() que, para que funcione, necesita una función para comparar dos valores. He escrito también, por lo tanto, la función de comparación (que puede ser tan simple como la del ejemplo, pero también mucho más compleja, depende de lo que se quiere lograr). La función respetaba, por supuesto, el prototipo provisto por mysort(): o sea, una función que necesita una callback, también tiene que declarar el prototipo de la callback misma (y si no ¿como la escribimos?). la mysort() misma se ocupa, pues, de llenar los dos parámetros de la callback con los valores a comparar.

Y ahora llegamos a la parte nueva: siempre refiriéndose al ejemplo de mysort() (que, imagino, ya conocéis de memoria) supongamos que necesitamos pasar otro parámetro a la callback, un parámetro externo disponible solo a nivel de la llamada principal, y que la mysort() no puede generar internamente.

¿Cómo podemos hacerlo? Veamos ahora el nuevo código (presentado como un solo bloque, pero, en realidad, a dividir en tres file):
/* parte que tendría que estar en el file mysort.h
*/
// prototipos para mySort()
typedef int (*mycallback)(int, int, void *);
void mySort(int *array, int nelems, mycallback cmpFunc, void *fp);

/* parte que tendría que estar en el file mysort.c
 */
// mySort() - funzione di sort che usa l'algoritmo bubblesort
void mySort(int *array, int nelems, mycallback cmpFunc, void *fp)
{
    // loop sobre todos los elementos de array
    while (nelems > 0) {
        // loop interno con decremento longitud
        int i;
        for (i = 0; i < (nelems - 1); i++) {
            // eseguo callback di comparazione
            if (cmpFunc(array[i], array[i + 1], fp)) {
                // eseguo swap di array[i] e array[i+1]
                int temp = array[i];
                array[i] = array[i + 1];
                array[i + 1] = temp;
            }
        }

        // decremento nelems
        nelems--;
    }
}

/* parte que tendría que estar en el file mymain.c
*/
// cbCmpFunc() - función de comparación
static int cbCmpFunc(int elem_a, int elem_b, void *fp)
{
    // escribo resultados parciales en un file
    fprintf(fp, "%d > %d = %d\n", elem_a, elem_b, elem_a > elem_b);

    return elem_a > elem_b;
}

// main
int main(void)
{
    int array[] = {34,12,32,9,10,72,82,23,14,7,94};
    int nelems = sizeof(array) / sizeof(int);
    FILE *fp = fopen("result.txt", "w");

    // ejecuto sort array
    mySort(array, nelems, cbCmpFunc, fp);

    // cierro file
    fclose(fp);

    // enseño resultados
    int i;
    for (i = 0; i < nelems; i++)
        printf("%d - ", array[i]);
    printf("\n");

    // esco
    return 0;
}
Como se puede ver es muy similar al ejemplo del otros post, sólo que ahora, a nivel de main(), abrimos un archivo para registrar datos de elaboración y tenemos que pasar el file descriptor a la mysort(), ya que no podemos pensar de escribir una función de librería que llevas imprimido el código el nombre del file de log: es la aplicación llamante que tiene que pasarlo.

¿Y cómo lo hacemos? Es muy simple: se añade un parámetro (del tipo oportuno) a mysort() y a la callback, y en la llamada principal (en el main(), en nuestro caso) se pasa el valor a la mysort(), que se ocupará de propagarlo hasta la callback, que es el usuario del nuevo parámetro; la mysort() no lo usa, lo transporta solamente. Con este método podemos pasar todos los parámetros que queremos: en el ejemplo he añadido uno, pero se pueden añadir al gusto.

Por supuesto, todo lo anterior es válido para las funciones que implementamos nosotros: no se puede pensar en añadir parámetros a funciones de libreria que no tenemos bajo control: por ejemplo, la qsort() sólo necesita la callback con dos parámetros, y así la tenemos que aguantar.

Alguien se puede preguntar porqué el parámetro fp es un void* y no un tipo más específico (en este caso un FILE*): lo he escrito así para demostrar que, con este método, se puede pasar cualquier valor (por ejemplo, en C++ se utiliza para pasar el puntero this): de hecho, he visto código donde se agregan void* a las callback* (en fase de proyecto) sólo para uso en el futuro, de manera de poder escribir, luego, callback muy personalizadas sin necesidad de cambiar la función base (que, como se ha dicho, sólo es transportadora de estos parámetros)

¿Qué os parece? Sí, también esta vez el tema suena un poco obvio, pero si un día os vais a pelear con las callback espero que os pueda ser útil. Yo, por ejemplo, por falta de documentación y otras razones inevitables, en su momento (hace mucho tiempo) tuve que aguantarme con sólo leer el código escrito por otros, y no sé lo que habría dado por tener a mi disposición un ejemplo tan simple como esto que acabo de proponer (pero, entonces, vivimos en una época de gran suerte... pero sólo para los informáticos. Y tampoco tanto. Pero esta es otra historia ...).

Hasta el próximo post.

viernes, 22 de marzo de 2013

C orientado a objetos II - La herencia
cómo usar la OOP en C - pt.2

Como prometido, volvemos en la escena del delito: después del sorprendente ejemplo de clase en C visto por aquí, hoy intentaremos implementar la herencia de clase en C. Os anticipo que todo lo que sigue es de poca utilidad, pero puede ser un tutorial útil sobre el potencial oculto de nuestro lenguaje favorito. Si no habéis leído la primera parte del post correr a leerla, sobre todo la parte de introducción, de lo contrario podríais pensar que me he vuelto loco para escribir cosas como ésta.

Vamos al grano: escribimos una (otra) clase elemental en C++ que implementa un (otro) contador.. Escribiremos, de acuerdo con la práctica clásica, ficheros de header, implementación y uso. Vemos l'header counter.hpp:
#ifndef COUNTER_HPP
#define COUNTER_HPP
/* counter.hpp - header clase CCounter
 */

class CCounter {
protected:
    // atributos privados
    int value;

public:
    // métodos públicos
    CCounter();          // constructor
    void incValue(int n);
    int getValue();
};
#endif /* COUNTER_HPP */
Una vez más, es una clase muy sencilla con un atributo privado, dos métodos de lectura y escritura y un constructor explícito. Pasamos al archivo de implementación, counter.cpp:
/* counter.cpp - implementación clase CCounter
 */
#include "counter.hpp"

// CCounter() - constructor de la clase CCounter
CCounter::CCounter()
{
    value = 100;
}

// incValue() - método di incremento atributo value
void CCounter::incValue(int n)
{
    value += n;
}

// getValue() - método de lectura atributo value
int CCounter::getValue()
{
    return value;
}
Esta vez, ya que estamos hablando de la herencia de clases, también tendremos una clase derivada, cuyo header es counter_der.hpp:
#ifndef COUNTER_DER_HPP
#define COUNTER_DER_HPP
/* counter_der.hpp - header clase CCounter_der
 */
#include "counter.hpp"

class CCounter_der : public CCounter {
public:
    // métodos públicos
    void incValue(int n);
};
#endif /* COUNTER_DER_HPP */
mientras que el file de implementación es counter_der.cpp:
/* counter_der.cpp - implementación clase CCounter_der
 */
#include "counter_der.hpp"

// incValue() - método de incremento atributo value
void CCounter_der::incValue(int n)
{
    value -= n;
}
Finalmente vemos el file de uso de la clase, countermain.cpp:
/* countermain.cpp - ejemplo de uso de las clases CCounter e CCounter_der
 */
#include <iostream>
#include "counter.hpp"
#include "counter_der.hpp"

using namespace std;

main()
{
    CCounter cnt;
    CCounter_der cnt_der;

    // leo valores
    cout << "cnt value = " << cnt.getValue() << endl;
    cout << "cnt_der value = " << cnt_der.getValue() << endl;

    // incremento valores
    cnt.incValue(5);
    cnt_der.incValue(7);

    // leo valores
    cout << "cnt value = " << cnt.getValue() << endl;
    cout << "cnt_der value = " << cnt_der.getValue() << endl;
}
El uso de la clase es un poco estúpido (como la última vez), pero para este ejemplo es más que suficiente. Creamos una instancia de dos objetos de la clase CCounter, cnt y cnt_der, imprimimos los valores de value para ambos objetos, incrementamos y reimprimimos: si compiláis y ejecutáis el programa la salida será:
cnt value = 100
cnt_der value = 100
cnt value = 105
cnt_der value = 93
Como se puede ver, por la magia de la herencia, el objeto cnt_der (una instancia de la simple CCounter_der) es un contador que disminuye, ya que la clase derivada se diferencia de la clase base (en este caso) solo por el método incValue(), así que al final, el valor del campo se incrementa en cnt y disminuye en cnt_der. Fantástico.

Tener en cuenta, que en los dos header-file he usado el oportuno include guard, para evitar inclusiones múltiples, ya que counter_der.h llama counter.h (de hecho, este ejemplo es un poco más riguroso respeto al del post anterior).

Una última nota: en la clase base el método incValue() no se ha definido virtual, sea porque para el ejemplo de uso propuesto no era necesario (el compilador, en realidad, no se queja...), sea porque cae fuera del objetivo del post: esto no es un blog sobre C++...

¿También la herencia de clase puede ser implementada en C? ¡Si! Pues bien, el resultado final no es (como se verá) muy elegante, pero funciona. Vamos a utilizar la misma estructura de header, implementación y uso. Vamos a empezar con el header ccounter.h:
#ifndef CCOUNTER_H
#define CCOUNTER_H
/* ccounter.h - header clase CCounter en C
 */

typedef struct _ccounter {
    // atributos
    int value;

    // métodos
    void (*construct)(struct _ccounter *this);    // constructor
    void (*incValue)(struct _ccounter *this, int n);
    int (*getValue)(struct _ccounter *this);
} CCounter;

// prototipos globales
void pubConstruct(CCounter *this);
#endif /* CCOUNTER_H */
Como se ha indicado en estas páginas es muy similar a ccounter.hpp. Pasamos ahora a la implementación en ccounter.c:
/* ccounter.c - implementación clase CCounter en C
 */
#include "ccounter.h"

// incValue() - método di incremento attributo value
static void incValue(CCounter *this, int n)
{
    this->value += n;
}

// getValue() - metodo de lectura atributo value
static int getValue(CCounter *this)
{
    return this->value;
}

// construct() - constructor de la clase CCounter
static void construct(CCounter *this)
{
    this->incValue = &incValue;
    this->getValue = &getValue;
    this->value = 100;
}

// pubConstruct() - constructor público de la clase CCounter
void pubConstruct(CCounter *this)
{
    this->construct = &construct;
    this->construct(this);
}
Como se ha indicado en el otro post, en comparación con la versión C++ tenemos que hacer explícitamente lo que el compilador de C++ hace  implícitamente: a cada método se pasa un puntero al objeto llamante, los métodos se asignan a los punteros de función, y tenemos que añadir método más, un constructor público, que hay que llamar después de la instanciación del objeto.

Y ahora llegamos a la parte más interesante, la clase derivada CCounter. Vemos el header ccounter_der.h:
#ifndef CCOUNTER_DER_H
#define CCOUNTER_DER_H
/* ccounter_der.h - header clase CCounter en C
 */
#include "ccounter.h"

typedef struct _ccounter_der {
    // atributos
    CCounter cnt_base;

    // métodos
    void (*construct)(struct _ccounter_der *this);    // constructor
    void (*incValue)(struct _ccounter_der *this, int n);
} CCounter_der;

// prototipos globales
void pubConstruct_der(CCounter_der *this);
#endif /* CCOUNTER_DER_H */
Como se puede ver es una versión simplificada de la cabecera de la clase base. En los atributos hay un objeto de tipo CCounter (este es el truco que nos permite hablar de herencia) y en los métodos se encuentra el usual constructor más el único método a redefinir (como en la versión C + +).

Pasamos ahora, a la implementación en ccounter.c:
/* ccounter.c - implementación clase CCounter_der en C
 */
#include "ccounter_der.h"

// incValue() - método de incremento atributo value
static void incValue(CCounter_der *this, int n)
{
    this->cnt_base.value -= n;
}

// construct() - constructor de la clase CCounter
static void construct(CCounter_der *this)
{
    this->incValue = &incValue;
}

// pubConstruct() - constructor público de la classe CCounter
void pubConstruct_der(CCounter_der *this)
{
    pubConstruct(&this->cnt_base);
    this->construct = &construct;
    this->construct(this);
}
Creo que el código es muy claro: también esta es una versión más pequeña de la implementación de la clase base, con la redefinición del método incValue() y su asignación. Tener en cuenta que el constructor publico llama al constructor público de la clase base. Sólo tenemos que ver el fichero de uso, ccountermain.c:
/* ccountermain.c - ejemplo de uso de la clase CCounter en C
 */
#include <stdio.h>
#include "ccounter.h"
#include "ccounter_der.h"

main()
{
    CCounter cnt;
    pubConstruct(&cnt);

    CCounter_der cnt_der;
    pubConstruct_der(&cnt_der);

    // leo valores
    printf("cnt value = %d\n", cnt.getValue(&cnt));
    printf("cnt_der value = %d\n", cnt_der.cnt_base.getValue(&cnt_der.cnt_base));

    // incremento valores
    cnt.incValue(&cnt, 5);
    cnt_der.incValue(&cnt_der, 7);

    // leo valores
    printf("cnt value = %d\n", cnt.getValue(&cnt));
    printf("cnt_der value = %d\n", cnt_der.cnt_base.getValue(&cnt_der.cnt_base));
}
Pues bien, también esta vez es virtualmente idéntico a la versión C++, a excepción de la llamada explícita al constructor público, y al uso de printf() para imprimir los resultados. Si compiláis y ejecutáis, el resultado (¡juro otra vez!) es idéntico al  de la versión C++. Como se quería demostrar, también  con el C podemos usar (¿simular? ¿emular?) la herencia de clases.

Ya que me he extendido un poco con el código, y, como siempre, no quiero aburrir a nadie, dejo para el próximo post las disquisiciones filosóficas, que había prometido, sobre C vs C++ (sí, no me he olvidado...), donde voy a tratar de ser lo más diplomático posible...

Hasta el próximo post.