2012-06-27 13 views
5

Fondamentalmente, quello che voglio è una sorta di versione generata in fase di compilazione che è associata con la definizione esatta di una struttura. Se la definizione della struttura cambia in qualsiasi modo (campo aggiunto, spostato, , forse rinominato), voglio che anche quella versione cambi.Genera ID versione della definizione struct?

Tale costante di versione sarebbe utile quando si legge in una struttura precedentemente serializzata, per assicurarsi che sia ancora compatibile. L'alternativa sarebbe tenere traccia manualmente di una costante specificata manualmente, che ha effetti potenzialmente confusi se l'incremento viene dimenticato (deserializzazione produce spazzatura), e solleva anche la domanda quando esatto per incrementarlo (durante lo sviluppo e il test, o solo durante qualche tipo di rilascio).

Questo potrebbe essere ottenuto utilizzando uno strumento esterno per generare un hash sulla definizione della struct, ma mi chiedo se sia possibile con il compilatore C (e/o forse il suo preprocessore) stesso.

Questa è in realtà una forma di introspezione e quindi sospetto che ciò potrebbe non essere possibile in ANSI C, ma sarei felice con una soluzione che funzioni con gcc e clang.

risposta

2

L'API di Windows utilizzato per (lo fa ancora?) Avere un membro della dimensione come uno dei primi membri di una struct, in modo che si sapeva che cosa versione del struct veniva passata (vedi WNDCLASSEX come esempio):

struct Foo 
{ 
    size_t size; 
    char *bar; 
    char *baz; 
    /* Other fields */ 
}; 

E prima di chiamare impostare la dimensione utilizzando sizeof:

struct Foo f; 

f.size = sizeof(struct Foo); 
f.bar = strdup("hi"); 
f.baz = strdup("there"); 

somefunc(&f); 

Poi somefunc saprebbe, in base alla size membro, quale versione del struct è a che fare. Poiché sizeof viene valutato in fase di compilazione anziché in fase di esecuzione, ciò consente la compatibilità ABI all'indietro.

+0

Qualsiasi riferimento MS a questo? – ouah

+0

Aggiunto un esempio –

+0

Grazie. Ho pensato solo a usare sizeof, ma ovviamente non mi preoccupo dei campi che vengono spostati o di altre operazioni che risultano in una dimensione vista in precedenza. Per l'API di Windows, è probabilmente una buona idea limitare le modifiche solo alle aggiunte di campi, ma attualmente non voglio arrivare a tanto ... –

2

Non c'è nulla che lo farebbe automaticamente, ma è possibile creare qualcosa che funzioni in modo ragionevolmente affidabile: è possibile utilizzare sizeof e offsetof e combinarli in modo tale che l'ordine in cui li si combina importa. Ecco un esempio:

#include <stdio.h> 
#include <stddef.h> 

#define COMBINE2(a,b) ((a)*31+(b)*11) 
#define COMBINE3(a,b,c) COMBINE2(COMBINE2(a,b),c) 
#define COMBINE4(a,b,c,d) COMBINE2(COMBINE3(a,b,c),d) 

typedef struct A { 
    int a1; 
    char a2; 
    float a3; 
} A; 

typedef struct B { 
    int b1; 
    char b2; 
    double b3; 
} B; 

typedef struct C { 
    char c2; 
    int c1; 
    float c3; 
} C; 

typedef struct D { 
    int d1; 
    char d2; 
    float d3; 
    int forgotten[2]; 
} D; 

int main(void) { 
    size_t aSign = COMBINE4(sizeof(A), offsetof(A,a1), offsetof(A,a2), offsetof(A,a3)); 
    size_t bSign = COMBINE4(sizeof(B), offsetof(B,b1), offsetof(B,b2), offsetof(B,b3)); 
    size_t cSign = COMBINE4(sizeof(C), offsetof(C,c1), offsetof(C,c2), offsetof(C,c3)); 
    size_t dSign = COMBINE4(sizeof(D), offsetof(D,d1), offsetof(D,d2), offsetof(D,d3)); 
    printf("%ld %ld %ld %ld", aSign, bSign, cSign, dSign); 
    return 0; 
} 

Questo codice prints

358944 478108 399864 597272 

Come si può vedere, questo codice produce costanti di runtime per ogni struttura che reagisce a ri-ordinamento dei campi di lunghezze e cambiando diversi tipi di campi. Reagisce anche all'aggiunta di campi anche se si dimentica di aggiornare l'elenco di campi su cui si basa il calcolo, che dovrebbe produrre una sorta di rete di sicurezza.

+0

Ah, mi sono dimenticato di 'offsetof'. Immagino che questa sia la cosa più vicina possibile con ANSI C? –

+0

@Julien Sì, penso che non si possa fare molto meglio che combinare questi due, almeno all'interno di ANSI C. Puoi comunque costruire un 'COMBINE' migliore: il mio sembra un lavoro veloce e sporco, anche se uso qualcosa come quello nel codice di produzione per l'hashing di varie strutture. – dasblinkenlight

+0

@Julien Se non si dispone di una limitazione per cui la funzione deve essere calcolata in fase di compilazione, è possibile evitare la ridenominazione calcolando l'hash su stringhe che rappresentano i nomi dei campi della struct (è necessario utilizzare l'operatore di stringa # preprocessore ## per quello). – dasblinkenlight

Problemi correlati