基本上,我想要的是某种编译时生成的版本,它与结构的确切定义相关联。如果结构的定义以任何方式发生变化(字段已添加,移动,可能重命名),我也希望该版本发生变化。
这样的版本常量在读取先前序列化的结构时非常有用,以确保它仍然兼容。另一种方法是手动跟踪一个手动指定的常量,如果忘记递增它会产生混乱效果(反序列化会产生垃圾),并且在准确地增加它时会提出问题(在开发和测试期间,或仅在某种情况下)释放)。
这可以通过使用外部工具在结构定义上生成哈希来实现,但我想知道C编译器(和/或它的预处理器)本身是否可行。
这实际上是某种形式的内省,所以我怀疑这在ANSI C中可能根本不可能,但我会对使用gcc和clang的解决方案感到满意。
答案 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)
没有任何东西可以自动完成,但你可以构建一些合理可靠的东西:你可以使用sizeof
和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;
}
此代码prints
358944 478108 399864 597272
如您所见,此代码为每个结构生成运行时常量,这些常量对不同长度的字段和更改字段类型的重新排序作出反应。即使您忘记更新计算基础的字段列表,它也会对添加字段作出反应,这应该会产生某种安全网。