我有一个相当奇怪的问题:我需要维护一大块涉及结构的代码。需要通过不时向其添加字段来修改这些结构。添加到结构的 end 是你应该做的,每当你在一个结构的 middle 中添加一个字段时,你就破坏了代码一种非平凡的方式。
维护变成了一场噩梦,因为这些需要偶尔进行修改,并且退出修改这些结构(以及由于前面字段的布局更改导致它们中断)的唯一方法是完全重写一大堆代码,这根本不可能。
我需要做的是找到一种方法来改变struct的布局是一个编译错误。现在,我可以这样解决问题:
struct Foo
{
int *Bar;
int Baz;
};
#ifdef _x86_
static_assert(_offsetof(Foo, Bar) == 0);
static_assert(_offsetof(Foo, Baz) == 4);
#else
static_assert(_offsetof(Foo, Bar) == 0);
static_assert(_offsetof(Foo, Baz) == 8);
#endif
然而,这是一项庞大的工作,因为这些结构中大约有20个,每个结构有4-10个字段。我可以通过断言最后一个字段的偏移位于正确的位置来最小化这个,但这仍然是很多手工工作。
有没有人对编译器技巧,C ++模板元程序技巧或其他方面有任何建议,这会使这更容易?
答案 0 :(得分:4)
你应该使用一种结构继承(c-style)形式,让每个人都更容易理解
基本上你会有你不想修改的结构
//DO not modify this structure!!!
struct Foo
{
int *Bar;
int Baz;
};
以及人们可以随意修改的FooExtensions(或其他)结构
struct FooExtensions
{
struct Foo base;
//go crazy but keep base as the first thing.
};
然后将FooExtnesions对象传递给期望Foo将其转换为Foo的方法,成员对齐将会解决。这是维持这种向后兼容性的更常见模式。显然,人们仍然可以忽略惯例,但这使得成功更容易。
你仍然可以添加所有static_asserts以确保不修改Foo
答案 1 :(得分:3)
如果解释代码中的情况的注释不会执行,则代码中的断言或其他任何内容都不会。这将是会发生的事情:
程序员将字段添加到结构的中间 程序员从断言(或其他聪明的方法)获得编译错误 程序员更改断言(或其他聪明的方法),以便在中间添加
: - )
我最喜欢的是当人们改善'精心设计的物品'时,只需让私人会员公开并随意写信给他们。
更好的想法在源代码管理中实施规则,防止恶意更改代码的转变。您必须编写一个在turnin上运行并保存结构的程序,然后在下次比较它们看看它们是如何变化的,然后让源代码控制运行它你可以告诉它是否有效。也许就像fxcop会为.net做的那样(是吗?)
更容易 - 只需要在其中包含结构的来源,让您在签入之前检查它们(取消他们的许可,无需您的批准检查它们;取决于您的源代码控制)。
答案 2 :(得分:1)
添加评论
// New field must be added at the end of the struct
答案 3 :(得分:1)
如果字段添加在错误的位置,我认为您使用断言生成编译时错误是正确的。但是,我建议编写一个脚本来为您生成断言代码(以避免所有手动工作)。
另一个解决方案是添加注释,指示添加到结构的正确方法。当有人修改不正确时,使用您的源代码管理系统来识别有罪的开发人员。加入你团队的其他成员公开嘲笑开发人员。让他们在一天的剩余时间里戴上超大的,傻傻的帽子。出去吃午饭,除了他们之外邀请所有人。最有可能的是,他们只会犯一次错误。
答案 4 :(得分:0)
老实说,这似乎不是编译器或预处理器的工作。
这似乎是源代码控制脚本的工作。 使用添加了中间结构的字段签入新版本应该会失败并显示相应的错误消息。
除此之外,你可以使用单元测试。将结构置零,将您期望的最后一个字段的值设置为单向非零(使用预期的偏移量或struct.fieldname)并以另一种方式进行测试。
答案 5 :(得分:0)
似乎运行良好的技术是对结构的元素进行编号,如下所示,
struct Foo
{
int *Bar; // Element 1
int Baz; // Element 2
long Bab; // Element 3
char Ban; // Element 4
int Bag; // Element 5
// ADD NEW ELEMENTS HERE
};
这使得添加到中间并且必须修复注释似乎更加繁琐。还应该清楚,秩序很重要。
我会查看是否有工具或脚本来生成代码的断言。