2012-06-27 14 views
5

Básicamente, lo que quiero es algún tipo de versión generada en tiempo de compilación que esté asociada con la definición exacta de una estructura. Si la definición de la estructura cambia de alguna manera (campo agregado, movido, cambiado de nombre), quiero que esa versión también cambie.¿Generar ID de versión de la definición de estructura?

Dicha constante de versión sería útil al leer en una estructura previamente serializada, para asegurarse de que aún es compatible. La alternativa sería hacer un seguimiento manual de una constante especificada manualmente, que tiene efectos potencialmente confusos si incrementarla se olvida (deserializar produce basura), y también plantea la pregunta cuándo exactamente incrementarla (durante el desarrollo y las pruebas, o solo durante algún tipo de lanzamiento).

Esto podría lograrse mediante el uso de una herramienta externa para generar un hash sobre la definición de estructura, pero me pregunto si es posible con el compilador de C (y/o tal vez su preprocesador).

Esto es realmente una forma de introspección y entonces sospecho que esto no puede ser posible en absoluto en ANSI C, pero estaría contento con una solución que funciona con gcc y clang.

Respuesta

2

La API de Windows solía (¿todavía lo hace?) Tener un miembro de tamaño como uno de los primeros miembros de una estructura, para saber qué versión de la estructura se estaba pasando (ver WNDCLASSEX como ejemplo):

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

Y antes de llamar ajusta el tamaño usando sizeof:

struct Foo f; 

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

somefunc(&f); 

Entonces somefunc sabría, basado en el miembro size, qué versión de la estructura que estaba tratando. Debido a que sizeof se evalúa en tiempo de compilación en lugar de en tiempo de ejecución, esto permite la compatibilidad ABI hacia atrás.

+0

¿Alguna MS hace referencia a esto? – ouah

+0

Agregó un ejemplo –

+0

Gracias. Pensé simplemente en usar sizeof, pero eso por supuesto no protege contra los campos que se mueven u otras operaciones que resultan en cualquier tamaño visto anteriormente. Para la API de Windows, probablemente sea una buena idea restringir las modificaciones solo a las adiciones de campos, pero actualmente no quiero llegar tan lejos ... –

2

No hay nada que lo haga de forma automática, pero puede construir algo que funcione de manera razonablemente confiable: puede usar sizeof y offsetof, y combinarlos de manera tal que el orden en que los combine importó. Aquí está un ejemplo:

#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; 
} 

Este código prints

358944 478108 399864 597272 

Como se puede ver, este código produce constantes de tiempo de ejecución para cada estructura que reacciona a la reordenación de campos de diferentes longitudes y cambiante tipos de campos. También reacciona a la adición de campos, incluso si olvida actualizar la lista de campos en los que basa su cálculo, lo que debería generar algún tipo de red de seguridad.

+0

Ah, me olvidé de 'offsetof'. Supongo que esto es lo más parecido posible con ANSI C? –

+0

@Julien Sí, creo que no se puede hacer mucho mejor que combinar estos dos, al menos dentro de ANSI C. Sin embargo, puedes construir una mejor 'COMBINACIÓN': la mía parece un trabajo rápido y sucio, aunque utilizo algo como eso en el código de producción para hash varias estructuras. – dasblinkenlight

+0

@Julien Si no tiene la restricción de que la función debe calcularse en tiempo de compilación, podría protegerse contra el cambio de nombre al calcular hash en cadenas que representan nombres de los campos de la estructura (tendría que usar '#' preprocessor stringification operator para eso). – dasblinkenlight

Cuestiones relacionadas