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

jueves, 24 de enero de 2013

¿Prototipos? Sí, ¡gracias!
cómo usar los Prototipos en C

Tras los excesos de las fiestas (y, tal vez, con un par de kilos acumulados por eliminar), es mejor empezar con un tema ligero ligero: los Prototipos de Funciones. Ligero, pero no demasiado.

Después de una inspección rápida en la red me he dado cuenta de que hay una cierta confusión sobre el tema. Prototipos necesarios, quizás recomendados, a veces desconocidos... Me he dado cuenta de información dudosa hasta en apuntes Universitarios (¡ay, ay!). Por cierto, en mi pasado, he hablado con varios colegas C-Programmers que no tenían ideas claras sobre el tema. Bueno, entonces es el momento de aclarar!

Vamos a empezar con los hechos, dejando para la segunda parte las consideraciones técnicas/filosóficas. Recomiendo prestar atención, en el siguiente texto, en ciertas palabras claves que usamos y vamos a tratar de ilustrar: declaración, prototipo y definición. Y, nos referiremos a las distintas versiones de C que nos han acompañado hasta la fecha, lo que a su vez son: K&R C, ANSI C (C89/C90) y C99 (estaría, también, el C11, pero no es significativo para este post). A menos que se especifique diversamente todos las próximas afirmaciones/consideraciones se refieren al C actual, el C99.

Vamos al grano: en C los prototipos no son obligatorios. La confusión sobre este tema proviene de la doble personalidad que muchos programadores de C (yo incluido) que, a menudo, tiene que desenredarse entre C y C++, haciendo, a veces, un poco de confusión: los prototipos son obligatorios en C++, por razones estrechamente relacionadas con ciertas características del lenguaje (¿os suena el Function Overloading?).

En C, sin embargo, es obligatoria la declaración de una función.

Hagamos, pues, un ejemplo sobre las palabras claves declaración, prototipo y definición, usando sólo un tipo moderno de sintaxis (ANSI C o C99):
// declaración de función
int myFunc();

// declaración de función con prototipo
int myFunc(int val):

// definición de función con prototipo
int myFunc(int val)
{
   if (val > 5)
       return val;
   else
       return val * 2;
}
El orden en este ejemplo, como es evidente, no es casual: la declaración es el caso básico, el prototipo contiene implícitamente una declaración, y, por último, la definición contiene implícitamente un prototipo (y por lo tanto también una declaración). Como se mencionó, en el ejemplo he omitido, para no complicar innecesariamente la descripción, sintaxis permitidas pero demasiado old-fashioned, o prohibidas por el C99.

Antes de trasladarse a la parte filosófica, hacemos un breve análisis histórico: en K&R C no había obligación de declarar las funciones, así que no había ninguna comprobación en compile-time sobre el valor de retorno y, menos aún, sobre la consistencia de los parámetros pasados: en ausencia de la declaración, el compilador aplicaba un comportamiento por defecto asumiendo que la función devolvía un int. Para los parámetros se aplicaba el default argument promotion: los enteros se promoven en int, y los float se promoven en doble.

Con la llegada de ANSI C (o C89/C90), han llegado los prototipos, pero se ha mantenido la compatibilidad hacia atrás con la sintaxis antigua (para no obligar a revisar millones de líneas de código funcionante). Con esta novedad era, finalmente, posible comprobar en compile-time el uso correcto de la funciones, sea para los parámetros sea para los valores devueltos. Debido a la compatibilidad hacia atrás se mantuvo, sin embargo, la posibilidad de escribir nuevo código con la sintaxis antigua, y, además, seguía siendo válido el concepto de default return value en ausencia de declaración.

Con el C99 se ha dado otro paso adelante: vale con la búsqueda de compatibilidad con el código existente, pero el valor de retorno por defecto era un agujero demasiado grande en la solidez del lenguaje, por lo tanto se ha introducido la declaración obligatoria como se indicaba al principio del post (añado que también se hizo obligatorio el uso de prototipos en los standard headers del lenguaje, pero esa es otra historia...).

Y ahora, después de describir lo que el standard nos exige y/o permite hacer, llegamos finalmente a lo que es mejor hacer: Creo que un buen programador utiliza prototipos (de ahí, supongo, para la propiedad transitiva aquellos que no utilizan prototipos no son buenos programadores. He dicho supongo, así que si alguien se ha ofendido, no se lo tome conmigo, se lo tome con la propiedad transitiva). ¿Y por qué recomiendo encarecidamente el uso de prototipos? Bueno, C es un lenguaje tipizado, por lo tanto es tan obvio la ayuda que este mecanismo nos puede dar para producir código sin errores de tipo, al tiempo que mejora la legibilidad y facilidad de mantenimiento, que no hay necesidad de explicarlo!

Y, para añadir un toque de radicalidad que nunca sobra, añado que, para las mismas cuestiones de legibilidad y mantenibilidad del software, no es conveniente contar con el hecho de que usando definiciones con prototipo (véase el ejemplo anterior), y escribiendo el código en el orden correcto (es decir, utilizando una función sólo después de su definición), no es necesario escribir prototipos reales. No seáis perezoso en cosas útiles, por favor!

¿Y como debe de estar estructurado un buen código considerando lo dicho anteriormente? Veamos un pequeño ejemplo con tres archivos: un header, un archivo con las funciones, y un archivo que las utiliza:

Este es el archivo header:
/* myfuncs.h
 */
// prototipos globales
char *myFunc1(char *dest, const char *src);
char *myFunc2(char *dest, const char *src);
Aquí está el archivo con las funciones:
/* myfuncs.c
 */
#include "myfuncs.h"

// myFunc1()
char *myFunc1(char *dest, const char *src)
{
    ...
}

// myFunc2()
char *myFunc2(char *dest, const char *src)
{
    ...
}
Y, finalmente, el archivo utilizador:
/* usefuncs.c
 */
#include "myfuncs.h"

// prototipos locales
static int useFuncs(void);
static int anotherFunc(void);

// anotherFunc()
static int anotherFunc(void)
{
    ...
    int res = useFuncs();
    ...
}

// useFuncs()
static int useFuncs(void)
{
    ...
    char *p1 = myFunc1(dest, src);
    char *p2 = myFunc2(dest, src);
    ...
}
¡Y ya no hace falta decir nada más!

Hasta el próximo post.