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

martes, 3 de diciembre de 2013

Bitwise operations: ep.II - El ataque de los shift
cómo usar los operadores Bitwise en C - pt.2

Ok, como había prometido aquí, volvemos al tema del bitwise para describir algunos resultados inesperados y proporcionar algunos ejemplos sencillos.

Vamos a empezar con las dolientes notas: Como se mencionó en el anterior post, las multiplicación y división con shift, dependiendo del tamaño del tipo del operando y la presencia o ausencia del bit de signo, pueden dar resultados inesperados. Vamos a ver un ejemplo:
void main()
{
    // 1) SHIFT hacia la izquierda con unsigned int
    unsigned int a, b;
    a = 74;           // 0 0 1 0 0 1 0 1 0
    b = a << 2;       // 1 0 0 1 0 1 0 0 0 resultado b=296
    printf("var = %d; var << 2 = %d\n", a, b);

    // 2) SHIFT hacia la izquierda con unsigned char
    unsigned char c, d;
    c = 74;           // 0 1 0 0 1 0 1 0
    d = c << 2;       // 0 0 1 0 1 0 0 0 resultado d=40
    printf("var = %d; var << 2 = %d\n", c, d);

    // 3) SHIFT hacia la derecha con signed char (o int)
    char e, f;
    e = -4;           // 1 1 1 1 1 1 0 0
    f = e >> 2;       // 1 1 1 1 1 1 1 1 resultado f=-1
    printf("var = %d: var >> 2 = %d\n", e, f);
    // N.B.: sin extensión de signo sería:
    // e = -4;        // 1 1 1 1 1 1 0 0
    // f = e >> 2;    // 0 0 1 1 1 1 1 1 resultado f=63
}
El caso 1 es claramente el caso funcionante: un int es mucho más grande que los 8 bits utilizados para representar el número inicial (74), por lo que el shift no pierde ningún 1 en el lado izquierdo (este es el posible problema) y el resultado es correcto (74x4 = 296). Sin embargo, si utilizamos (caso 2) un char (8 bits) durante el shift perdemos un 1 y el resultado va en overflow (¿¿ 74x4 = 40 ??). Así que ¡tengan cuidado!

El caso 3, además, es aún más insidioso: hacer operaciones con signo (en los casos 1 y 2 utilicé variables unsigned en preparación del paso 3) y usando valores negativos, pueden pasar cosas extrañas: para la representación misma de los números negativos en binario (complemento a 2) el bit más a la izquierda (MSB) es el bit de signo, y, en este caso la operación de shift es machine-dependent: en función del tipo de CPU puede tener o no la extensión de signo (por defecto, por ejemplo, en las máquinas de Intel), por eso el shift del ejemplo puede dar el resultado esperado (-4/4 = -1) o un resultado completamente diferente (¿¿ -4/4 = 63 ??). Una vez más, ¡tener cuidado!

Y ahora es el momento de mostrar algunos ejemplos prácticos de uso de lo anterior, que de otro modo seria know-how sin sentido: vamos a ver cómo leer el estado de cada bits de una word utilizando una máscara:
void main()
{
    // uso de una mascara para leer los bit de una word
    unsigned char mask = 1;     // 0 0 0 0 0 0 0 1
    unsigned char word = 74;    // 0 1 0 0 1 0 1 0

    // loop de lectura
    int i;
    for (i = 0; i < 8; i++)
        printf("el bit %d de la word es %s\n",
                i, (word & mask<<i) ? "ON" : "OFF");
}
sencillo, ¿no? Y lo mismo se puede hacer con una macro:
#define INPUT(w, i)    (w & 0x01<<i)

void main()
{
    // uso de una macro para leer los bit de una word
    unsigned char i_word = 74;    // 0 1 0 0 1 0 1 0

    // loop de lectura
    int i;
    for (i = 0; i < 8; i++)
        printf("el bit %d de la word es %s\n",
                i, INPUT(i_word, i) ? "ON" : "OFF");
}
Y luego, ya que esto es un blog de estilo, vemos una manera con una buena estética para leer lo input de un dispositivo, por ejemplo los finales de carrera de un sistema electromecánico que tenemos que controlar con nuestro querido C:
#define INPUT_FC1    (in_word & 0x01<<0)
#define INPUT_FC2    (in_word & 0x01<<1)

void main()
{
    // uso de una define para leer un bit en una word
    unsigned char in_word = 74;    // 0 1 0 0 1 0 1 0

    // lectura
    printf("el bit FC1 de la word es %s\n", INPUT_FC1 ? "ON" : "OFF");
    printf("el bit FC2 de la word es %s\n", INPUT_FC2 ? "ON" : "OFF");
}
Aquí, el ejemplo que acabamos de enseñar indica una manera, simple y elegante para describir los input (utilizando unos mnemónicos auto-explicativos) que pueden ser útiles para escribir el Software de control de dispositivos Hardware, fácil de leer y mantener.

Fácil de leer y mantener: ya he escuchado esto... ¡ah! sí: ​​es como debe ser todo el S/W que escribe un Analista Programador (y lástima que muchos analistas programadores con minúscula se olvidan de este imperativo cuando se sientan delante de un ordenador...).

¡Hasta el próximo post!

miércoles, 13 de noviembre de 2013

¡Bitwise operations!
cómo usar los operadores Bitwise en C - pt.1

Aunque éste es un blog de estilo (por si alguien todavía no se había dado cuenta...), he decidido, durante un tiempo, continuar con mi trabajo meritorio (¡que modestia!) de actualizar algunos temas que me imagino que todo el mundo ha leído y/o estudiado (tal vez en el K&R), y luego han rápidamente olvidado, porque seamos sinceros, el C contiene partes bastantes especializadas y de uso marginal, que en algunos entornos de programación no se utilizan nunca (¡nunca nunca!).

Repetimos el juego del post anterior: que levante la mano quien ha escrito recientemente código que utiliza las operaciones bit a bit. 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 operaciones bit a bit. Uhmm ... Veo pocas manos levantadas. El hecho es que las bitwise opeartions son 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 (y he usado exactamente las mismas palabras del post anterior: soy ecologista, cuando puedo reciclo).

En comparación con las union, por otra parte, hay que decir que sobre las operaciones bit a bit el fantástico K&R no es particularmente claro y detallado, y las trata (como son) como un tema secundario, y no te involucra con decenas de ejemplos entretenidos que te ayudan en memorizar perfectamente el argumento, de hecho, que yo recuerde de la última vez que lo leí, hay solo un par de páginas que te se escapaban de las manos y que ya has olvidado cuando pasas al siguiente capítulo. Bueno, la biblia K&R (que considero sagrada) tiene algunos puntos no tan atractivos.

Refrescamos: los operadores de bitwise (que operan en los bits individuales) son:
"&"  AND
"|"  OR
"^"  XOR
"~"  NOT (complemento a 1)
"<<" SHIFT hacia la izquierda
">>" SHIFT hacia la derecha
  
NOTA: 
- el NOT es un operador unario: funciona con un solo argumento indicado 
  a la derecha. 
- los operadores de shift son operadores unarios: operan con un solo 
  argumento indicado a la izquierda.  
Ahora bien, sin entrar en discursos aburridos, nos dirigimos a una pequeña tabla y a algunos ejemplos sencillos.

Aquí está la tabla:
"& ":  el resultado es 1 si los dos operandos son 1. En caso contrario 0. 
"|" :  el resultado es 0 si los dos operandos son 0. En caso contrario 1. 
"^ ":  el resultado es 1 si los dos operandos son diferentes. En caso contrario 0. 
"~ ":  el resultado es 1 si el operando es 0. En caso contrario 0. 
"<<n": el resultado es el operando con todos los bits desplazados a la 
       izquierda por n posiciones. 
">>n": el resultado es el operando con todos los bits desplazados a la 
       derecha por n posiciones.
Y aquí están los simples ejemplos prácticos:
AND
int a = 74;       // 0 1 0 0 1 0 1 0
int b = 174;      // 1 0 1 0 1 1 1 0
int c = a & b;    // 0 0 0 0 1 0 1 0 resultado c=10

OR
int a = 74;       // 0 1 0 0 1 0 1 0
int b = 174;      // 1 0 1 0 1 1 1 0
int c = a | b;    // 1 1 1 0 1 1 1 0 resultado c=238

XOR
int a = 74;       // 0 1 0 0 1 0 1 0
int b = 174;      // 1 0 1 0 1 1 1 0
int c = a & b;    // 1 1 1 0 0 1 0 0 resultado c=228

NOT
int a = 74;       // 0 1 0 0 1 0 1 0
int b = ~b;       // 1 0 1 1 0 1 0 1 resultado c=181

SHIFT hacia la izquierda
int a = 74;       // 0 1 0 0 1 0 1 0
int b = a<<2;     // 0 0 1 0 1 0 0 0 resultado c=296

SHIFT hacia la derecha
int a = 74;       // 0 1 0 0 1 0 1 0
int b = a>>2;     // 0 0 0 1 0 0 1 0 resultado c=18
Tener en cuenta que en las operaciones de shift los bits nuevos que entran por la derecha (en el shift hacia la izquierda) valen 0, y los bit nuevos que entran por la izquierda (en el shift hacia la derecha) valen 0.

Tener en cuenta también que el shift hacia la derecha es equivalente a una división por múltiplos de 2 (>>1 es una división por 2, >>2 es una división por 4, etc.) , Mientras que el shift hacia la izquierda es equivalente a una multiplicación por múltiplos de 2 (<<1 es una multiplicación por 2, <<2 es una multiplicación por 4, etc.). Estas operaciones de multiplicación y división son muy rápidas, y se puede tener la tentación de utilizarlas para acelerar el código: bueno, antes de hacerlo, leer de nuevo (o leer) esto.

Y me gustaría añadir una advertencia: en función del tamaño del tipo del operando, y la presencia o ausencia del bit de signo, las multiplicaciones y divisiones con shift pueden dar resultados inesperados. Hablaremos de esto en el próxima episodio, donde vamos a hacer unos cuantos ejemplos prácticos de código que utiliza las bitwise operations (y no contengáis la respiración, mientras tanto...).

Hasta el próximo post.

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. 

martes, 19 de febrero de 2013

¡C orientado a objetos! ¿C orientado a objetos?
cómo usar la OOP en C

El tema del post de hoy es un oxímoron. C es un lenguaje absolutamente procedural (me atrevería a decir que es EL lenguaje procedural), por lo que pensar en hacer Object Oriented Programming con C no tiene mucho sentido ... Pero es posible, y sin molestar a su hijo C++! Voy a presentar un ejemplo sencillo sencillo en C++ (absolutamente fácil), y trataremos de ver su equivalente en C. Por último, os anticipo, voy a escribir algunas notas filosóficas sobre C, C++ y programación orientada a objetos. Pero, tal vez, mientras escribo este post me aburro y decido no escribirlas, o, quizás, se me olvida y no las escriba (solución diplomática: si escribo lo que tengo en mente ahora alguien podría sentirse "tocado"...).

Condición necesaria (y suficiente) para continuar con la lectura del post, es la posesión de ciertos conocimientos de C++ y programación orientada a objetos: si no los tenéis podéis inscribiros a un curso intensivo de C++ o leer rápidamente un manual... sin embargo, dada la simplicidad del ejemplo, no tratéis de convertiros en super-expertos de C++ para terminar de leer el post. Porque sino, en este caso (super-expertos!), podríais necesitar tanto tiempo que podría no existir más este blog o internet ... ("life is too long to be good at C++", Erik Naggum).

Vamos al grano: escribimos una clase elementar en C++ que implementa un contador. Escribiremos, de acuerdo con la práctica clásica, en tres ficheros: header, implementación y uso. Vemos el header counter.hpp:
/* counter.hpp - header clase CCounter
 */
class CCounter {
private:
    // atributos privados
    int value;

public:
    // métodos públicos
    CCounter();          // constructor
    void incValue(int n);
    int getValue();
};
Según lo prometido, es una clase simple 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 = 0;
}

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

// getValue() - método de lectura atributo value
int CCounter::getValue()
{
    return value;
}
Obviamente, no hay ni siquiera que explicar cómo funcionan los métodos anteriores (y, si todavía estáis leyendo, ya debería haber terminado con éxito, el curso intensivo de C++...). Finalmente vemos el fichero de uso de la clase, countermain.cpp:
/* countermain.cpp - ejemplo de uso de la clase CCounter
 */
#include <iostream>
#include "counter.hpp"

using namespace std;

main()
{
    CCounter cnt_a;
    CCounter cnt_b;

    // leo valores
    cout << "cnt_a value = " << cnt_a.getValue() << endl;
    cout << "cnt_b value = " << cnt_b.getValue() << endl;

    // incremento valores
    cnt_a.incValue(5);
    cnt_b.incValue(7);

    // leo valores
    cout << "cnt_a value = " << cnt_a.getValue() << endl;
    cout << "cnt_b value = " << cnt_b.getValue() << endl;
}
Ok, el uso de la clase es un poco estúpido, pero para este ejemplo es más que suficiente. Instanciamos dos objetos de la clase CCounter, cnt_a y cnt_b, imprimimos los valores de value para ambos objetos, incrementamos y reimprimimos: si compiláis y ejecutáis el programa la salida será:
cnt_a value = 0
cnt_b value = 0
cnt_a value = 5
cnt_b value = 7
Como se puede ver, por la magia de programación orientada a objetos y del C++ los dos objetos tienen vida propia y, al final, el campo value tiene un valor diferente en los dos objetos (mérito del legendario puntero this... pero esto ya debéis saberlo y esto no es un curso de C++, así que no voy a explicarlo).

¿Todo esto también se puede hacer en C? ¡Si! Bueno, no del todo idéntico, pero casi. Vamos a utilizar la misma estructura en tres ficheros. Vamos a empezar con el header 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;
Se ve muy similar a ccounter.hpp, ¿verdad? Obviamente no hay las palabras mágicas public y private, y los métodos son espléndidos (?) Punteros de función, sin embargo, la similitud es evidente. Pasamos a la implementación en ccounter.c:
/* ccounter.c - implementación clase CCounter en C
 */
#include "ccounter.h"

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

// getValue() - método 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 = 0;
}

// pubConstruct() - constructor publico de la clase CCounter
void pubConstruct(CCounter *this)
{
    this->construct = &construct;
    this->construct(this);
}
Aquí las diferencias son más evidentes, pero la similitud estructural es igualmente clara. En comparación con la versión de C++ tenemos que hacer explícitamente lo que el compilador C++ hace de forma implícita: a cada método se pasa un puntero al objeto llamante (extraño, lo he llamado this, aunque no sé por qué, será algo inconsciente), los métodos se asignan a los punteros de función, y tenemos que añadir un método más, un constructor público que se llamará después de la instanciación del objeto (también esto el compilador de C++ lo hace implícitamente).

Sólo tenemos que ver el archivo de uso, ccountermain.c:
/* ccountermain.c - ejemplo de uso de la clase CCounter en C
 */
#include <stdio.h>
#include "ccounter.h"

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

    CCounter cnt_b;
    pubConstruct(&cnt_b);

    // leo valores
    printf("cnt_a value = %d\n", cnt_a.getValue(&cnt_a));
    printf("cnt_b value = %d\n", cnt_b.getValue(&cnt_b));

    // incremento valores
    cnt_a.incValue(&cnt_a, 5);
    cnt_b.incValue(&cnt_b, 7);

    // leo valores
    printf("cnt_a value = %d\n", cnt_a.getValue(&cnt_a));
    printf("cnt_b value = %d\n", cnt_b.getValue(&cnt_b));
}
Bueno, esto es prácticamente idéntico a la versión C++, a excepción de la llamada explícita al constructor público y el uso de printf() para imprimir los resultados. Si compiláis y ejecutáis, el resultado (¡juro!) es idéntico al  de la versión C++.

En el próximo post, os mostraré cómo se puede implementar, en C, la herencia de clases (¡wow!). Ahora no tengo ganas, y me alargaría demasiado (no quiero aburrir a nadie). Además, como se predijo en el principio del post, no voy a escribir las disquisiciones filosóficas... pero esas, también, están sólo aplazadas hasta el próximo post.

Antes de saludarnos no puedo evitar escribir una pequeña nota de reflexión (pero ¡no perdéis el sueño pensando en esa!): el ejemplo mostrado no tiene mucha utilidad (en realidad, ¡ninguna!). Pero es una interesante demostración de la flexibilidad y potencia de nuestro amado C, y, añado, una demostración real de las orígenes del C++: tal vez, no todo el mundo sabe que hace muuuchos años (años '80) algunos de los compiladores C++ en el mercado eran, en realidad, unos mega-preprocesadores que leían el código C++, lo transformaban en C y lo compilaban, luego, con un normal compilador C. Entonces, el ejemplo anterior representa (más o menos...) lo que hacían los compiladores C++ primordiales, cuando el C++ no era más que un C a objetos. Meditar gente, meditar...

Hasta el próximo post.