P99: Un aiuto per la macro programming - STB1019/SkullOfSummer GitHub Wiki

Introduzione

La macro programming può essere vista come un modo per effettuare "code generation", ossia programmi che creano codice sorgente, anziché eseguibile. Per esempio, la macro:

#define _CAT(x, y) x ## y
#define CAT(x,y) _CAT(x, y)
#define GENERATE_LIST_CELL(type) \
struct CAT(type, _cell) { \
    type payload; \
    struct CAT(type, _cell)* next; \
}

permette di generate strutture che possono essere utilizzate in una "forward list":

GENERATE_LIST_CELL(int)
struct int_cell {
    int payload;
    struct int_cell* next;
}

GENERATE_LIST_CELL(long)
struct long_cell {
    long payload;
    struct long_cell* next;
}

La programmazione macro è potente, ma a volte mentre la si usa si vorrebbe avere a disposizione delle funzioni che normalmente non si sa. Un esempio è ottenere il numero di argomenti all'interno di una macro variadica:

FOO(1,2,3,4) # return 4
FOO(a, b, c, d, e) # return 5
FOO() # return 0

P99 è un progetto opensource essenziale se si vuole utilizzare al massimo la macro programming. Il progetto è fatto completamente di header ed è un eccellente modo per imparare un uso spintissimo di macro programming. Lo sviluppatore però può semplicemente sfruttare il progetto.

P99

P99 è un progetto disponibile:

P99 è un progetto enorme, ma noi discuteremo soltanto di alcuni funzioni particolarmente utili.

Gestione delle macro variadiche

Uno dei principali usi di P99 è quello di aiutare il programmatore mentre scrive in macro programming. Tra le funzioni importanti possiamo citare P99_NARG, P99_IF_EQ, P99_FOR

P99_NARG(...)

è una macro che ti permette di generare il numero di argomenti variadici passati. Per esempio:

P99_NARG(10, 20, 30, 40) //4
P99_NARGS() // 0

P99_FOR

Sicuramente una tra le macro più utili in tutto P99. La macro consente di generare codice sorgente a partire da una sequenza di numeri. Il suo uso è il seguente:

#define _REDUCE(NAME, I, REC, RES) REC + RES
#define _MAP(NAME, X, I) X
P99_FOR(A, 4, _REDUCE, _MAP, 5, 6, 7, 9);

--> 5 + 6 + 7 +9

Come funziona? Possiamo dividere P99_FOR in 3 parti:

P99_FOR(
    //prima parte
    A, 4, 
    //seconda parte
    _REDUCE, _MAP, 
    // terza parte
    5, 6, 7, 9
);
  • la prima parte contiene una variabile (in questo caso A) che verrà inviata a tutte le chiamate di _REDUCE e _MAP. Pensa a questa variabile come una sorta di contesto accessibile ad ogni chiamata; In questa parte vengono anche dati in ingresso il numero di elemento su cui ciclare;
  • la seconda parte contiene le 2 macro, _MAP e _REDUCE: _MAP contiene il codice da generate per ogni elemento della lista. Reduce invece è il codice da generare quando si vuole unire quello finora generato con un nuovo elemento di _MAP;
  • la terza parte contiene invece la sequenza di elementi su cui ciclare.

Dall'esempio di prima:

#define _REDUCE(NAME, I, REC, RES) REC + RES
#define _MAP(NAME, X, I) (X)
P99_FOR(PIPPO, 4, _REDUCE, _MAP, 5, 6, 7, 9);

Ecco le chiamate di _MAP:

NAME X I _MAP(NAME, X, I)
PIPPO 5 0 (5)
PIPPO 6 1 (6)
PIPPO 7 2 (7)
PIPPO 8 3 (8)

Non vi piace di P99_FOR richieda il numero di argomenti? Nessun problema! Usiamo P99 per risolvere questo piccolo inghippo:

#define FOREACH(CONTEXT, _MAP, _REDUCE, ...) P99_FOR(CONTEXT, P99_NARG(__VA_ARGS__),  _REDUCE, _MAP, ## __VA_ARGS__)

P99_IF_EQ

è una macro che ti permette di testare se 2 parole (non valori, parole: il preprocessore non ha accesso ad alcuna struttura dati né valori del compilatore) sono uguali. Per esempio

P99_IF_EQ(0, X)(hello)(world)

Se #define X 0 allora la macro verrà espansa nel contenuto della seconda parentesi:

P99_IF_EQ(0, X)(hello)(world) --> hello

Se invece X viene espanso in qualcosa d'altro, allora la macro verrà espansa nel contenuto della terza parentesi:

P99_IF_EQ(0, X)(hello)(world) --> world

Cosa può fare un uso spinto di P99?

P99 può essere incredibilmente utile per:

  • implementare un framework per la reflection in C (notare che di base C non possiede modi per avere la reflection);
  • simulare deallocazione automatica dei puntatori;

Referenze