生成结构定义的版本ID?

时间:2012-06-27 21:37:15

标签: c gcc clang

基本上,我想要的是某种编译时生成的版本,它与结构的确切定义相关联。如果结构的定义以任何方式发生变化(字段已添加,移动,可能重命名),我也希望该版本发生变化。

这样的版本常量在读取先前序列化的结构时非常有用,以确保它仍然兼容。另一种方法是手动跟踪一个手动指定的常量,如果忘记递增它会产生混乱效果(反序列化会产生垃圾),并且在准确地增加它时会提出问题(在开发和测试期间,或仅在某种情况下)释放)。

这可以通过使用外部工具在结构定义上生成哈希来实现,但我想知道C编译器(和/或它的预处理器)本身是否可行。

这实际上是某种形式的内省,所以我怀疑这在ANSI C中可能根本不可能,但我会对使用gcc和clang的解决方案感到满意。

2 个答案:

答案 0 :(得分:2)

以前的Windows API(仍然如此?)有一个size成员作为结构的第一个成员之一,因此它知道它传递的结构的版本(参见WNDCLASSEX作为示例):

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

在致电之前,请使用sizeof设置尺寸:

struct Foo f;

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

somefunc(&f);

然后somefunc将根据size成员知道它正在处理的结构版本。因为在编译时而不是运行时评估sizeof,所以这允许向后ABI兼容性。

答案 1 :(得分:2)

没有任何东西可以自动完成,但你可以构建一些合理可靠的东西:你可以使用sizeofoffsetof,并以这样的方式组合它们结合它们很重要。这是一个例子:

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

此代码prints

358944 478108 399864 597272

如您所见,此代码为每个结构生成运行时常量,这些常量对不同长度的字段和更改字段类型的重新排序作出反应。即使您忘记更新计算基础的字段列表,它也会对添加字段作出反应,这应该会产生某种安全网。