c++测试验证相等操作符随时间保持与结构体一致

C++ test to verify equality operator is kept consistent with struct over time

本文关键字:结构体 时间 验证 测试 操作符 c++      更新时间:2023-10-16

我投票给@TomalakGeretkal,因为他写了一篇关于合同的好文章;我还没有接受答案,因为我的问题是如何以编程方式检查相等函数。


我有一个POD结构&一个相等算子,一个(非常)小的部分,系统有>100个工程师。

随着时间的推移,我希望结构被修改(成员添加/删除/重新排序),我想写一个测试来验证相等性op正在测试结构的每个成员(例如随着结构的变化而保持最新)。

正如Tomalak指出的那样-评论&"通过合同"通常是执行这一点的最佳/唯一方式;然而,在我的情况下,我预计会出现问题,并希望探索是否有任何方法可以主动捕获(至少许多)修改。

我没有想出一个令人满意的答案——这是我想到的最好的答案:

-new up two instances struct (x, y), fill each with identical non-zero data.
-check x==y
-modify x "byte by byte"
    -take ptr to be (unsigned char*)&x
    -iterator over ptr (for sizeof(x))
        -increment the current byte
        -check !(x==y)
        -decrement the current byte
        -check x==y

如果相等运算符捕获了每个字节,则测试通过(注意:对此有一个警告-并非所有字节都用于编译器表示的x,因此测试必须'跳过'这些字节-例如硬代码忽略字节)

我建议的测试有明显的问题:(至少)"不关心"字节,并且在x中增加一个字节的类型可能不会导致该内存位置的变量的有效值。

有更好的解决方案吗?

(这应该无关紧要,但我使用VS2008, rtti是关闭的,googletest套件)

虽然用这样的自检来使代码"万无一失"很诱人,但根据我的经验,让自检本身万无一失是徒劳的。

保持简单,并使任何更改的效果本地化。在结构定义中写一个注释,明确表示如果结构为;然后,如果这失败了,这只是程序员的错。

知道这对您来说似乎不是最优的,因为它在将来可能会导致用户错误,但实际上您无法绕过这一点(至少在不使您的代码变得非常复杂的情况下),通常最实用的方法是不麻烦。

我同意(并支持)Tomalak的回答。你不太可能找到一个万无一失的解决方案。尽管如此,一种简单的半自动方法是在相等操作符内验证预期的大小:

MyStruct::operator==(const MyStruct &rhs) 
{
  assert(sizeof(MyStruct) == 42);  // reminder to update as new members added
  // actual functionality here ...
}

这样,如果添加了任何新成员,assert将被触发,直到有人更新相等操作符。当然,这并非万无一失。(成员变量可能被替换为相同大小的东西,等等)尽管如此,它是一个相对简单的(一行断言),可以很好地检测错误情况。

我知道我肯定会被投反对票,但是…

如果一个模板等价函数接受一个int形参的引用,并且测试两个对象呢?相等函数将返回bool值,但会将size引用(int)增加sizeof(T)。

然后有一个大的测试函数,调用每个对象的模板并求和总大小——>将这个和与对象的大小进行比较。虚函数/继承等的存在可能会扼杀这个想法。

这实际上是一个很难在自测中正确解决的问题。

我能想到的最简单的解决方案是使用几个操作多种类型的模板函数,执行必要的转换、提升和比较,然后在外部单元测试中验证结果。当一个重大的变更被引入时,至少你会知道。

使用组合等方法比使用扩展/子类更容易维护/验证这些挑战。

同意Tomalak和Eric的观点。我用它来解决类似的问题。

除非定义了DEBUG,否则

Assert不起作用,因此您可能会发布错误的代码。这些测试并不总是可靠地工作。如果结构中包含位字段,或者插入了由于编译器对齐字边界而占用空闲空间的项,则大小不会改变。因此,它们提供的价值有限。例如

struct MyStruct {   
    char a ;
    ulong l ;
}

改为

struct MyStruct {   
    char a ;
    char b ;  
    ulong l ;
}

两个结构都是8字节(在32位Linux x86上)