5

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

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

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

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

4

2 回答 2

2

没有什么可以自动完成,但是您可以构建一些合理可靠地工作的东西:您可以使用sizeofand offsetof,并以这样一种方式组合它们,即您组合它们的顺序很重要。这是一个例子:

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

此代码打印

358944 478108 399864 597272

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

于 2012-06-27T22:00:09.107 回答
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 兼容性。

于 2012-06-27T21:41:22.070 回答