Nei titoli e nei testi troverete qualche rimando cinematografico (ebbene si, sono un cinefilo). Se non vi interessano fate finta di non vederli, già che non sono fondamentali per la comprensione dei post...

Di questo blog ho mandato avanti, fino a Settembre 2018, anche una versione in Spagnolo. Potete trovarla su El arte de la programación en C. Buona lettura.

domenica 1 dicembre 2013

Bitwise operations: Ep.II - L'attacco degli shift
come usare gli operatori Bitwise in C - pt.2

Ok, come promesso qui, torniamo sull'argomento bitwise per descrivere alcuni risultati inaspettati e fornire alcuni semplici esempi pratici.

Cominciamo dalle dolenti note: come anticipato nel post precedente, le moltiplicazioni e divisioni con shift, in base alla dimensione del tipo del operando e alla presenza o meno del bit di segno, possono dare risultati inaspettati. Vediamo un esempio:
void main()
{
    // 1) SHIFT a sinistra 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 risultato b=296
    printf("var = %d; var << 2 = %d\n", a, b);

    // 2) SHIFT a sinistra 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 risultato d=40
    printf("var = %d; var << 2 = %d\n", c, d);

    // 3) SHIFT a destra 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 risultato f=-1
    printf("var = %d: var >> 2 = %d\n", e, f);
    // N.B.: senza estensione di segno sarebbe:
    // e = -4;        // 1 1 1 1 1 1 0 0
    // f = e >> 2;    // 0 0 1 1 1 1 1 1 risultato f=63
}
Il caso 1 è, evidentemente il caso funzionante: un int è molto più grande degli 8 bit usati per rappresentare il numero di partenza (74), per cui lo shift non perde nessun 1 sulla sinistra (è questo il possibile problema) e il risultato è corretto (74x4=296). Se usiamo però (caso 2) un char (8 bit) durante lo shift perdiamo un 1 e il risultato va in overflow (74x4=40 ??). Quindi attenzione!

Il caso 3, poi, è ancora più subdolo: facendo operazioni con segno (nei casi 1 e 2 ho usato variabili unsigned proprio in preparazione al punto 3) e usando valori negativi, possono succedere cose strane: per la rappresentazione stessa dei numeri negativi in binario (complemento a 2) il bit più a sinistra (MSB) è il bit di segno, e, in questo caso l'operazione di shift è machine-dependent: in base al tipo di CPU possiamo disporre o no dell'estensione di segno (di default, ad esempio, su macchine Intel), per cui lo shift dell'esempio può dare il risultato aspettato (-4/4=-1) o un risultato completamente diverso (-4/4=63 ??). Di nuovo: attenzione!

E adesso è il momento di mostrare alcuni esempi pratici di uso di quanto esposto, che altrimenti, sarebbe know-how fine a se stesso: vediamo come leggere lo stato dei singoli bit di una word usando una maschera:
void main()
{
    // uso di una maschera per leggere i bit di 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 di lettura
    int i;
    for (i = 0; i < 8; i++)
        printf("il bit %d della word è %s\n",
                i, (word & mask<<i) ? "ON" : "OFF");
}
semplice no? E la stessa operazione si può fare con una macro:
#define INPUT(w, i)    (w & 0x01<<i)

void main()
{
    // uso di una macro per leggere i bit di una word
    unsigned char i_word = 74;    // 0 1 0 0 1 0 1 0

    // loop di lettura
    int i;
    for (i = 0; i < 8; i++)
        printf("il bit %d della word è %s\n",
                i, INPUT(i_word, i) ? "ON" : "OFF");
}
E poi, visto che questo è un blog di stile, vediamo un modo con una una buona estetica per leggere degli input di un dispositivo, per esempio i fine corsa di un sistema elettromeccanico che dobbiamo controllare col nostro amato C:
#define INPUT_FC1    (in_word & 0x01<<0)
#define INPUT_FC2    (in_word & 0x01<<1)

void main()
{
    // uso di una define per ogni bit da leggere di una word
    unsigned char in_word = 74;    // 0 1 0 0 1 0 1 0

    // lettura
    printf("il bit FC1 della word è %s\n", INPUT_FC1 ? "ON" : "OFF");
    printf("il bit FC2 della word è %s\n", INPUT_FC2 ? "ON" : "OFF");
}
Ecco, l'esempio appena mostrato indica una maniera, semplice ed elegante, per descrivere degli input (usando dei mnemonici auto-esplicativi) che può risultare utile per scrivere del Software di controllo di dispositivi Hardware, facile da leggere e da manutenere.

Facile da leggere e da manutenere: questa l'ho già sentita... ah si: è come dovrebbe essere tutto il S/W che scrive un Analista Programmatore (e peccato che molti analisti programmatori con la minuscola si scordano di questo imperativo quando si siedono davanti a un computer...).

Ciao e al prossimo post!

martedì 12 novembre 2013

Bitwise operations!
come usare gli operatori Bitwise in C - pt.1

Anche se questo è un blog di stile (nel caso che qualcuno non se ne fosse ancora accorto...), ho deciso, ancora per un po', di continuare con la mia opera meritoria (che modestia!) di rinfrescare alcuni argomenti che, immagino, tutti hanno letto e/o studiato (magari sul K&R), e poi hanno prontamente dimenticato, perché, diciamocelo, il C contiene delle parti un po' specialistiche e di nicchia, che in alcuni ambienti di programmazione non si usano mai (ma proprio mai!).

Ripetiamo il giochetto dello scorso post: alzi la mano chi ha scritto recentemente codice che usa le operazioni bit a bit. Oppure, alzi la mano, senza prima andare a rileggersi un manuale del C, chi di voi sa usare e/o descrivere perfettamente le operazioni bit a bit. Uhmm... vedo poche mani alzate. Il fatto è che le bitwise operations sono una di quelle parti del C un po' misconosciute, di uso dubbio e infrequente, insomma una di quelle parti di cui, per mancanza di pratica ci si scorda (e ho usato esattamente le stesse parole dello scorso post: sono un ecologista, quando posso riciclo).

Rispetto alle union, oltretutto, devo dire che sulle operazioni bit a bit il fantastico K&R non è particolarmente chiaro e dettagliato, le tratta (come sono) come un argomento di nicchia, e non ti coinvolge con decine di divertenti esempi che ti aiutano a memorizzare definitivamente l'argomento, anzi, per quel che ricordo dell'ultima volta che lo lessi, sono un paio di pagine che scivolano via e che hai già dimenticato quando passi al prossimo capitolo. Beh anche la bibbia K&R (che io considero sacra) ha alcuni punti non proprio coinvolgenti.

Rinfreschiamo: gli operatori di bitwise (che operano sui singoli bit) sono:
"&"  AND
"|"  OR
"^"  XOR
"~"  NOT (complemento a 1)
"<<" SHIFT a sinistra
">>" SHIFT a destra  
     
N.B.:
- il NOT e' un operatore unario: opera su un solo argomento indicato
  sulla destra.
- gli shift sono operatori unari: operano su un solo argomento indicato
  sulla sinistra.
Adesso, senza dilungarci in noiosi sproloqui, passiamo a una piccola tabella e ad alcuni semplici esempi pratici.

Ecco la tabella:
"&":   il risultato è 1 se i due operandi valgono 1. Altrimenti 0.
"|":   il risultato è 0 se i due operandi valgono 0. Altrimenti 1.
"^":   il risultato è 1 se i due operandi sono diversi. Altrimenti 0.
"~":   il risultato è 1 se l'operando vale 0. Se l'operando vale 1 il risultato è 0.
"<<n": il risultato è l'operando con tutti i bit spostati a sinistra di
       n posizioni. 
">>n": il risultato è l'operando con tutti i bit spostati a destra di
       n posizioni.
Ed ecco i semplici esempi pratici:
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 risultato 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 risultato 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 risultato c=228

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

SHIFT a sinistra
int a = 74;       // 0 1 0 0 1 0 1 0
int b = a<<2;     // 0 0 1 0 1 0 0 0 risultato b=296

SHIFT a destra
int a = 74;       // 0 1 0 0 1 0 1 0
int b = a>>2;     // 0 0 0 1 0 0 1 0 risultato b=18
Notare che nelle operazioni di shift i bit nuovi che entrano a destra (nello shift a sinistra) valgono 0, e i bit nuovi che entrano a sinistra (nello shift a destra) valgono 0.

Notare anche che lo shift a destra equivale a una divisione per multipli di 2 (>>1 è una divisione per 2, >>2 è una divisione per 4, ecc.), mentre lo shift a sinistra equivale a una moltiplicazione per multipli di 2 (<<1 è una moltiplicazione per 2, <<2 è una moltiplicazione per 4, ecc.). Queste operazioni di moltiplicazione e divisione sono molto veloci, e si potrebbe essere tentati a usarle per velocizzare il codice: beh, prima di farlo rileggetevi (o leggetevi) questo.

E aggiungo un avvertimento: in base alla dimensione del tipo del operando e alla presenza o meno del bit di segno, le moltiplicazioni e divisioni con shift possono dare risultati inaspettati. Di questo ne parleremo in una prossima puntata, in cui faremo qualche esempio pratico di codice che usa le bitwise operations (e non trattenete il respiro, nel frattempo...).

Ciao e al prossimo post.

domenica 13 ottobre 2013

La union fa la forza
come usare le union in C

Alzi la mano chi ha scritto recentemente codice che usa le union. Oppure, alzi la mano, senza prima andare a rileggersi un manuale del C, chi di voi sa usare e/o descrivere perfettamente le union ? Uhmm... vedo poche mani alzate. Il fatto è che le union sono una di quelle parti del C un po' misconosciute, di uso dubbio e infrequente, insomma una di quelle parti di cui, per mancanza di pratica, ci si scorda.

Per introdurvi l'argomento (e, ripeto, leggete senza consultare prima un manuale del C, se no non potete mettervi alla prova), vi racconterò una storia vera. Un tipo che conosco, le cui iniziali sono A.A. (di più non posso dirvi per questioni di privacy) andò, molto tempo fa, a un colloquio di lavoro, e gli proposero un semplice test come questo (più o meno):
#include <stdio.h>

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

    my_test.i = 0x01020304;

    // cosa stampa la seguente linea?
    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]);
}
Ecco, per i motivi indicati all'inizio del post (uso dubbio e infrequente), il nostro amico che non usava una union da almeno 15 anni (si, 15 anni!), rimase inizialmente perplesso, sforzò la memoria e non cadde nel 1º livello di errore che induce questo test un po' ingannevole, 1º livello di errore che ti indurrebbe a dire:

ho inizializzato il campo i e vado a leggere il campo s: ma allora la printf() stampa valori indefiniti!

No, lui si ricordava che "le union allocano memoria per la più grande delle variabili interne, variabili che condividono lo stesso spazio di memoria", quindi non cadde nel tranello, e rispose quasi perfettamente... quasi, perché cadde nel 2º livello di errore, quello che ti porta a dire:

ho inizializzato i, però leggo s, che è come se fosse stata inizializzata anche lei, quindi la riposta è: s[0]=1 s[1]=2 s[2]=3 s[3]=4 !

Ok, quasi giusto: in realtà la risposta è:

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

Il nostro amico A.A. se ne rese conto tornando a casa, quando l'errore ormai era fatto: nella locazione s[0] ci finisce la parte meno significativa di i (quindi 0x04), e così via gli altri numeri, per cui il risultato era in ordine inverso. Va beh, cose che succedono...

Se cercate in rete con google troverete tanti utili esempi d'uso delle union, tra i quali, direi, i più interessanti sono quelli per il trattamento di dati con contenuto variabile (protocolli di comunicazione, per esempio) in cui si vuole trattare, con la stessa struttura dati, informazioni di tipo diverso, e interpretando la union, di volta in volta, nella maniera corretta selezionando un opportuno flag mantenuto dal programma. Questo permette di fare programmi flessibili e risparmiosi di memoria (rispetto all'uso delle struct), cosa che in alcuni ambienti di sviluppo potrebbe risultare utile.

Occhio però: per la maniera stessa in cui funzionano le union sono flessibili ma ingannevoli, e gli errori subdoli di funzionamento sono dietro l'angolo che aspettano. Bisogna essere molto rigorosi nel mantenere, e testare, il flag di stato che ci indica quale personalità della union stiamo usando in ogni momento.

Ciao e al prossimo post.

Questo post è ispirato ad una storia vera, ma ogni riferimento a fatti o persone realmente esistiti è puramente casuale (uhmm ???)

domenica 28 aprile 2013

La maledizione della Callback di giada II - Il ritorno
come scrivere una Callback in C - pt.2

Ho deciso di prendermi un attimo di pausa sulla questione del C a oggetti: ho già scritto due post sull'argomento e non voglio annoiarmi né annoiarvi. Lo so, si potrebbero approfondire altri dettagli e parlare (come promesso) di C vs C++, ma, in questo momento, non ne ho voglia. Comunque, prima o poi, ci torneremo di sicuro...

Per cui, oggi, ritorniamo su un argomento già trattato qui, e su cui (mi son reso conto proprio ieri) si potrebbe fare qualche aggiunta interessante. Sto parlando, nuovamente, delle funzioni callback.

Ovviamente, prima di continuare a leggere, dovreste rinfrescarvi la memoria rileggendovi l'altro post sull'argomento, perché questo ne è una estensione, e sono strettamente collegati.

Pausa di riflessione...

Siete già tornati? ma come ? Non avete ancor riletto il vecchio post? Dovete rileggerlo, grazie...

Altra pausa di riflessione...

Ecco, adesso possiamo continuare.

Allora, come avrete ri-notato, l'esempio che avevo proposto era, direi, classico, ispirato all'uso della qsort(), quindi con una callback che si chiama senza argomenti, ma che, in realtà, ne necessita due che verranno generati internamente dalla funzione stessa (la mysort() dell'esempio, o la qsort(), se preferite).

Quindi, riepiloghiamo il flusso dell'esempio: ho scritto un main() che usa la funzione mysort() che, per funzionare, ha bisogno di una funzione di comparazione tra due valori. Ho scritto anche, quindi, la funzione di comparazione (che può essere semplice come quella dell'esempio, ma anche molto più complessa, dipende da quello che si vuole ottenere). La funzione rispettava, ovviamente, il prototipo fornito con la mysort(): ossia, una funzione che ha bisogno di una callback, deve anche dichiarare il prototipo della calback stessa (e se no come la scriviamo?). La mysort() stessa si occupa, poi, di riempire i due parametri della callback con i valori da comparare.

E ora passiamo alla parte nuova: sempre riferendoci al nostro esempio della mysort() (che oramai conoscerete a memoria) supponiamo di avere bisogno di passare un altro parametro alla callback, un parametro esterno disponibile solo a livello della chiamata base, e che la mysort() non può generare internamente.

Come possiamo fare? Vediamo subito il nuovo codice (presentato come un blocco unico, ma, in realtà, da dividere su tre file):
/* parte che dovrebbe essere nel file mysort.h
*/
// prototipi per mySort()
typedef int (*mycallback)(int, int, void *);
void mySort(int *array, int nelems, mycallback cmpFunc, void *fp);

/* parte che dovrebbe essere nel file mysort.c
 */
// mySort() - funzione di sort che usa l'algoritmo bubblesort
void mySort(int *array, int nelems, mycallback cmpFunc, void *fp)
{
    // loop su tutti gli elementi di array
    while (nelems > 0) {
        // loop interno con lunghezza calante
        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 che dovrebbe essere nel file mymain.c
*/
// cbCmpFunc() - funzione di comparazione
static int cbCmpFunc(int elem_a, int elem_b, void *fp)
{
    // scrivo risultati parziali in 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");

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

    // chiudo file
    fclose(fp);

    // stampo risultati
    int i;
    for (i = 0; i < nelems; i++)
        printf("%d - ", array[i]);
    printf("\n");

    // esco
    return 0;
}
Come vedete è molto simile all'esempio dell'altro post, solo che ora, a livello del main(), apriamo un file per registrare dei dati di elaborazione e dobbiamo passare il file descriptor alla mysort(), già che non possiamo certo pensare di scrivere una funzione di libreria che porti schiantato nel codice il nome del file di log: è l'applicazione chiamante che deve passarlo.

E come lo passiamo? È abbastanza semplice: si aggiunge un parametro (del tipo opportuno) alla mysort() e alla callback, e, nella chiamata base (nel main(), nel nostro caso) si passa il valore alla mysort(), che si occuperà i propagarlo fino alla callback, che è l'utilizzatrice del nuovo parametro; la mysort() non lo usa, lo trasporta solamente. Con questo metodo possiamo passare tutti i parametri che vogliano: nell'esempio ne ho aggiunto uno, ma se ne possono aggiungere a piacere.

Ovviamente tutto quanto sopra è valido per funzioni che implementiamo noi: non si può certo pensare di aggiungere parametri a funzioni di libreria di cui non abbiamo il controllo: ad esempio la qsort() ha bisogno solo della callback con due parametri, e così ce la dobbiamo tenere.

Qualcuno si chiederà perché il parametro fp è un void* e non un tipo più specifico (in questo caso un FILE*) : l'ho scritto così per dimostrare che, con questo metodo, si può passare qualsiasi cosa (ad esempio in C++ si usa per passare il pointer this): anzi, ho visto codice in cui si aggiungono dei void* alle callback (in fase di progetto) solo per usi futuri, in maniera di poter scrivere, in seguito, delle callback molto personalizzate senza modificare la funzione base (che, come detto, e solo trasportatrice di questi parametri)

Che ve ne sembra? Si, anche questa volta l'argomento suona un po' lapalissiano, ma, se un giorno vi scontrerete con le callback spero vi torni utile. Io, ad esempio, per mancanza di documentazione e altri motivi contingenti, a suo tempo (molto tempo fa) ho dovuto arrangiarmi da solo leggendo codice scritto da altri, e non so cosa avrei dato per avere a disposizione un esempio semplice semplice come quello che vi ho appena proposto (ma, allora, viviamo in un'epoca fortunata... si, ma solo per gli informatici. E neanche tanto. Ma questa è un altra storia...).

Ciao e al prossimo post.

venerdì 8 marzo 2013

C orientato agli oggetti II - l'eredità
come usare la OOP in C - pt.2

Come promesso torniamo su luogo del delitto: dopo il mirabolante esempio di classe in C visto da queste parti, oggi cercheremo di implementare anche l'ereditarietà di classe in C. Vi anticipo che tutto quanto segue serve a poco, ma potrebbe essere una utile esercitazione sulle potenzialità nascoste del nostro linguaggio preferito. Se non avete ancora letto la prima parte del post correte a leggerla, specialmente la parte introduttiva, altrimenti potreste pensare che sono diventato matto a scrivere roba come questa.

Veniamo al dunque: scriviamo un (altra) classe elementare in C++ che implementa un (altro) contatore. Scriveremo, secondo la prassi classica, file di header, implementazione e uso. Vediamo l'header counter.hpp:
#ifndef COUNTER_HPP
#define COUNTER_HPP
/* counter.hpp - header classe CCounter
 */

class CCounter {
protected:
    // attributi privati
    int value;

public:
    // metodi pubblici
    CCounter();          // costruttore
    void incValue(int n);
    int getValue();
};
#endif /* COUNTER_HPP */
Anche questa volta è una classe semplicissima, con un attributo privato, due metodi di lettura e scrittura e un costruttore esplicito. Passiamo al file di implementazione, counter.cpp:
/* counter.cpp - implementazione classe CCounter
 */
#include "counter.hpp"

// CCounter() - costruttore della classe CCounter
CCounter::CCounter()
{
    value = 100;
}

// incValue() - metodo di incremento attributo value
void CCounter::incValue(int n)
{
    value += n;
}

// getValue() - metodo di lettura attributo value
int CCounter::getValue()
{
    return value;
}
Questa volta, visto che parliamo di ereditarietà di classe, avremo anche una classe derivata, il cui header file è counter_der.hpp:
#ifndef COUNTER_DER_HPP
#define COUNTER_DER_HPP
/* counter_der.hpp - header classe CCounter_der
 */
#include "counter.hpp"

class CCounter_der : public CCounter {
public:
    // metodi pubblici
    void incValue(int n);
};
#endif /* COUNTER_DER_HPP */
mentre il file di implementazione è counter_der.cpp:
/* counter_der.cpp - implementazione classe CCounter_der
 */
#include "counter_der.hpp"

// incValue() - metodo di incremento attributo value
void CCounter_der::incValue(int n)
{
    value -= n;
}
Per ultimo vediamo il file di uso della classe, countermain.cpp:
/* countermain.cpp - esempio d'uso delle classi CCounter e CCounter_der
 */
#include <iostream>
#include "counter.hpp"
#include "counter_der.hpp"

using namespace std;

main()
{
    CCounter cnt;
    CCounter_der cnt_der;

    // leggo valori
    cout << "cnt value = " << cnt.getValue() << endl;
    cout << "cnt_der value = " << cnt_der.getValue() << endl;

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

    // leggo valori
    cout << "cnt value = " << cnt.getValue() << endl;
    cout << "cnt_der value = " << cnt_der.getValue() << endl;
}
L'uso della classe è un po' stupido (come l'altra volta), ma per quest'esempio è più che sufficiente. Istanziamo due oggetti della classe CCounter, cnt e cnt_der, stampiamo i valori di value per entrambi gli oggetti, incrementiamo e ristampiamo: se compilate ed eseguite il programma l'uscita sarà:
cnt value = 100
cnt_der value = 100
cnt value = 105
cnt_der value = 93
Come vedete, per la magia della ereditarietà, l'oggetto cnt_der (istanza della semplicissima CCounter_der) è un contatore che decrementa, visto che la classe derivata differisce dalla classe base (in questo caso) solo per il metodo incValue(), per cui, alla fine, il campo value ha valore incrementato in cnt e decrementato in cnt_der. Fantastico.

Notare, che nei due header-file ho usato l'opportuna include guard, per evitare inclusioni multiple, visto che counter_der.h richiama counter.h (in effetti, questo esempio è un po' più rigoroso di quello del post precedente).

Una ultima nota: nella classe base il metodo incValue() non è stato definito virtual, sia perché per l'esempio d'uso proposto non era necessario (il compilatore, infatti, non si lamenta...), sia perché esula dall'obiettivo del post: non è mica un blog di C++ questo...

Anche l'ereditarietà si può implementare in C? Si! Beh, il risultato finale non sarà (come vedrete) elegantissimo, ma funziona. Useremo la stessa struttura a header, implementazione e uso. Cominciamo con l'header ccounter.h:
#ifndef CCOUNTER_H
#define CCOUNTER_H
/* ccounter.h - header classe CCounter in C
 */

typedef struct _ccounter {
    // attributi
    int value;

    // metodi
    void (*construct)(struct _ccounter *this);    // costruttore
    void (*incValue)(struct _ccounter *this, int n);
    int (*getValue)(struct _ccounter *this);
} CCounter;

// prototipi globali
void pubConstruct(CCounter *this);
#endif /* CCOUNTER_H */
Come già fatto notare su queste pagine somiglia molto a ccounter.hpp. Passiamo alla implementazione in ccounter.c:
/* ccounter.c - implementazione classe CCounter in C
 */
#include "ccounter.h"

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

// getValue() - metodo di lettura attributo value
static int getValue(CCounter *this)
{
    return this->value;
}

// construct() - costruttore della classe CCounter
static void construct(CCounter *this)
{
    this->incValue = &incValue;
    this->getValue = &getValue;
    this->value = 100;
}

// pubConstruct() - costruttore pubblico della classe CCounter
void pubConstruct(CCounter *this)
{
    this->construct = &construct;
    this->construct(this);
}
Come già fatto notare nell'altro post, rispetto alla versione C++ dobbiamo fare in maniera esplicita ciò che il compilatore C++ fa implicitamente: a ogni metodo passiamo un puntatore all'oggetto chiamante, i metodi sono assegnati ai puntatori a funzione, e dobbiamo aggiungere un metodo in più, un costruttore pubblico, da chiamare dopo l'istanziazione dell'oggetto.

E, adesso passiamo alla parte più interessante, la classe derivata CCounter_der. Vediamo l'header ccounter_der.h:
#ifndef CCOUNTER_DER_H
#define CCOUNTER_DER_H
/* ccounter_der.h - header classe CCounter in C
 */
#include "ccounter.h"

typedef struct _ccounter_der {
    // attributi
    CCounter cnt_base;

    // metodi
    void (*construct)(struct _ccounter_der *this);    // costruttore
    void (*incValue)(struct _ccounter_der *this, int n);
} CCounter_der;

// prototipi globali
void pubConstruct_der(CCounter_der *this);
#endif /* CCOUNTER_DER_H */
Come si nota è una versione ridotta dell'header della classe base. Negli attributi c'è un oggetto di tipo CCounter (è il trucco che ci permette di parlare di ereditarietà) e nei metodi c'è il solito costruttore più l'unico metodo da ridefinire (come nella versione C++).

Passiamo ora alla implementazione in ccounter.c:
/* ccounter.c - implementazione classe CCounter_der in C
 */
#include "ccounter_der.h"

// incValue() - metodo di incremento attributo value
static void incValue(CCounter_der *this, int n)
{
    this->cnt_base.value -= n;
}

// construct() - costruttore della classe CCounter
static void construct(CCounter_der *this)
{
    this->incValue = &incValue;
}

// pubConstruct() - costruttore pubblico della classe CCounter
void pubConstruct_der(CCounter_der *this)
{
    pubConstruct(&this->cnt_base);
    this->construct = &construct;
    this->construct(this);
}
Credo che il codice sia chiarissimo: anche questa è una versione ridotta dell'implementazione della classe base, con la ridefinizione del metodo incvalue() e del suo assegnamento. Notare che il costruttore pubblico richiama il costruttore pubblico della classe base. Ci resta solo da vedere il file d'uso, ccountermain.c:
/* ccountermain.c - esempio d'uso della classe CCounter in C
 */
#include <stdio.h>
#include "ccounter.h"
#include "ccounter_der.h"

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

    CCounter_der cnt_der;
    pubConstruct_der(&cnt_der);

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

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

    // leggo valori
    printf("cnt value = %d\n", cnt.getValue(&cnt));
    printf("cnt_der value = %d\n", cnt_der.cnt_base.getValue(&cnt_der.cnt_base));
}
Beh, anche questa volta è praticamente identico alla versione C++, salvo la chiamata esplicita al costruttore pubblico, e all'uso della printf() per la stampa dei risultati. Se compilate e eseguite, il risultato (giuro di nuovo!) è identico a quello della versione C++. Come volevasi dimostrare anche con il C possiamo usare (simulare? emulare?) l'ereditarietà di classe.

Visto che mi sono dilungato un po' con il codice e, come sempre, non voglio annoiare nessuno, rimando al prossimo post le disquisizioni filosofiche, che avevo promesso, su C vs C++  (si, non mi sono dimenticato...), dove cercherò di essere il più diplomatico possibile...

Ciao e al prossimo post.

domenica 17 febbraio 2013

C orientato agli oggetti! C orientato agli oggetti?
come usare la OOP in C

L'argomento del post di oggi è un ossimoro. Il C è un linguaggio decisamente procedurale (oserei dire che è IL linguaggio procedurale), per cui pensare di fare Object Oriented Programming con il C non ha molto senso... però è possibile, e senza scomodare il figlioletto C++! Presenterò un esempio semplice semplice in C++ (ma proprio semplice), e cercheremo di vedere il suo equivalente in C. Per concludere, vi anticipo, scriverò alcune note filosofiche su C, C++ e OOP. Poi, magari, mentre scrivo il post mi stufo e decido di non scriverle, oppure me ne dimenticherò e non le scriverò (soluzione diplomatica: se scrivo ciò che ho in mente adesso qualcuno potrebbe sentirsi "toccato"...).

Condizione necessaria (e sufficiente), per continuare a leggere il post, è il possesso di alcune nozioni di C++ e OOP: se non le avete iscrivetevi a un corso accelerato di C++ o leggetevi rapidamente un manuale... comunque, data la semplicità dell'esempio, non tentate diventare super-esperti del C++ per finire di leggere il post. Anche perché, in tal caso (super-esperti!), vi potrebbe servire così tanto tempo che potrebbe non esistere più né questo blog né internet... ("life is too long to be good at C++", Erik Naggum).

Veniamo al dunque: scriviamo una classe elementare in C++ che implementa un contatore. Scriveremo, secondo la prassi classica, su tre file: header, implementazione e uso. Vediamo l'header counter.hpp:
/* counter.hpp - header classe CCounter
 */
class CCounter {
private:
    // attributi privati
    int value;

public:
    // metodi pubblici
    CCounter();          // costruttore
    void incValue(int n);
    int getValue();
};
Come promesso è una classe semplicissima, con un attributo privato, due metodi di lettura e scrittura e un costruttore esplicito. Passiamo al file di implementazione, counter.cpp:
/* counter.cpp - implementazione classe CCounter
 */ 
#include "counter.hpp"

// CCounter() - costruttore della classe CCounter
CCounter::CCounter()
{
    value = 0;
}

// incValue() - metodo di incremento attributo value
void CCounter::incValue(int n)
{
    value += n;
}

// getValue() - metodo di lettura attributo value
int CCounter::getValue()
{
    return value;
}
Evidentemente non c'è neanche da spiegare come funzionano i metodi appena descritti (e, se state ancora leggendo, dovreste avere già fatto, con successo, il corso accelerato di C++...). Per ultimo vediamo il file di uso della classe, countermain.cpp:
/* countermain.cpp - esempio d'uso della classe CCounter
 */
#include <iostream>
#include "counter.hpp"

using namespace std;

main()
{
    CCounter cnt_a;
    CCounter cnt_b;

    // leggo valori
    cout << "cnt_a value = " << cnt_a.getValue() << endl;
    cout << "cnt_b value = " << cnt_b.getValue() << endl;

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

    // leggo valori
    cout << "cnt_a value = " << cnt_a.getValue() << endl;
    cout << "cnt_b value = " << cnt_b.getValue() << endl;
}
Ok, l'uso della classe è un po' stupido, ma per quest'esempio è più che sufficiente. Instanziamo due oggetti della classe CCounter, cnt_a e cnt_b, stampiamo i valori di value per entrambi gli oggetti, incrementiamo e ristampiamo: se compilate ed eseguite il programma l'uscita sarà:
cnt_a value = 0
cnt_b value = 0
cnt_a value = 5
cnt_b value = 7
Come vedete, per la magia della OOP e del C++ i due oggetti hanno vita propria, e, alla fine, il campo value ha valore diverso nei due oggetti (merito del mitico puntatore this... ma questo dovreste già saperlo e questo non è un corso di C++, quindi non lo spiegherò).

Tutto questo si può fare anche in C? Si! Beh, non proprio identico, ma quasi. Useremo la stessa struttura a tre file. Cominciamo con l'header ccounter.h:
/* ccounter.h - header classe CCounter in C
 */
typedef struct _ccounter {
    // attributi
    int value;

    // metodi
    void (*construct)(struct _ccounter *this);       // costruttore
    void (*incValue)(struct _ccounter *this, int n);
    int (*getValue)(struct _ccounter *this);
} CCounter;
Somiglia molto a ccounter.hpp, no? Ovviamente non ci sono le parole magiche public e private, e i metodi sono degli splendidi (?) puntatori a funzione, comunque la somiglianza è evidente. passiamo alla implementazione in ccounter.c:
/* ccounter.c - implementazione classe CCounter in C
 */
#include "ccounter.h"

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

// getValue() - metodo di lettura attributo value
static int getValue(CCounter *this)
{
    return this->value;
}

// construct() - costruttore della classe CCounter
static void construct(CCounter *this)
{
    this->incValue = &incValue;
    this->getValue = &getValue;
    this->value = 0;
}

// pubConstruct() - costruttore pubblico della classe CCounter
void pubConstruct(CCounter *this)
{
    this->construct = &construct;
    this->construct(this);
}
Qui le differenze sono più evidenti, però la somiglianza strutturale è altrettanto chiara. Rispetto alla versione C++ dobbiamo fare in maniera esplicita ciò che il compilatore C++ fa implicitamente: a ogni metodo passiamo un puntatore all'oggetto chiamante (che strano, l'ho chiamato this, anche se non so perché, sarà qualcosa di inconscio), i metodi sono assegnati ai puntatori a funzione, e dobbiamo aggiungere un metodo in più, un costruttore pubblico, da chiamare dopo l'instanziazione dell'oggetto (anche questo il compilatore C++ lo fa implicitamente).

Ci resta solo da vedere il file d'uso, ccountermain.c:
/* ccountermain.c - esempio d'uso della classe CCounter in C
 */
#include <stdio.h>
#include "ccounter.h"

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

    CCounter cnt_b;
    pubConstruct(&cnt_b);

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

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

    // leggo valori
    printf("cnt_a value = %d\n", cnt_a.getValue(&cnt_a));
    printf("cnt_b value = %d\n", cnt_b.getValue(&cnt_b));
}
Beh, questo è praticamente identico alla versione C++, salvo la chiamata esplicita al costruttore pubblico, e all'uso della printf() per la stampa dei risultati. Se compilate e eseguite, il risultato (giuro!) è identico a quello della versione C++.

Nel prossimo post, vi mostrerò come sia possibile implementare, in C, anche la ereditarietà delle classi (wow!). Adesso non ne ho voglia, e mi dilungherei troppo (non voglio annoiare nessuno). Anzi, come previsto all'inizio del post, non scriverò neanche le disquisizioni filosofiche... ma anche quelle sono solo rimandate al prossimo post.

Prima di lasciarci non posso evitare di scrivere una nota di riflessione (ma non perdeteci il sonno ripensandoci!): l'esempio appena mostrato serve a poco (anzi, a niente!). Però è una interessante dimostrazione di flessibilità e potenza del nostro amato C, e aggiungo, una dimostrazione reale delle origini del C++: forse non tutti sanno che, moooolti anni fa (anni '80) alcuni dei compilatori C++ disponibili sul mercato erano, in realtà, dei mega-preprocessori, che leggevano il codice C++, lo trasformavano in C, e lo compilavano con un normale compilatore C. Ecco, l'esempio qui sopra è (più o meno...) quello che facevano i compilatori C++ primordiali, quando il C++ era ancora solo un C a oggetti. Meditate gente, meditate...

Ciao e al prossimo post.