GTest和参数压栈顺序

18 Jan

有个reader,给定一个start index和stop index,它将读取natural start index(=stop index)区间中的数据。取natural start index和natural stop index的代码是通过如下的接口取得的:

reader.Invert(startIndex, stopIndex, &naturalStartIndex, &naturalStopIndex);

可见,为了实现读取一个更大区间的数据,Invert返回的区间要比原先的区间大。

当然,要取小区间的数据,只需要调用两次reader.Invert(…)即可:

reader.Invert(startIndex, startIndex, &dummy, &naturalStart);
reader.Invert(stopIndex, stopIndex, &naturalStop, &dummy);

可是legacy code犯了个错误,在某些本该取大区间的地方是这么调用reader.Invert(…)的:

reader.Invert(startIndex, startIndex, &naturalStart, &naturalStart);
reader.Invert(stopIndex, stopIndex, &naturalStop, &naturalStop);

而reader.Invert(…)对最后两个参数的赋值顺序是最后写最右侧的参数,于是导致最后取得的区间是一个右移的区间。

对于正确地区大区间的代码,加UT是很容易的:

EXPECT_CALL(mockObj, Invert(_, _, _, _))
.WillRepeatedly(DoAll(
WithArgs<0, 2>(Invoke(AssignAndDecreaseBy(delta))),
WithArgs<1, 3>(Invoke(AssignAndIncreaseBy(delta))),
Return(S_OK)));

其中AssignAndIncreaseBy和QAssignAndDecreaseBy是两个functor:void (*f)(long* assign, long delta)。当调用时,第3个指针指向的值会被减小,而第4个指针指向的值会被增大,这样就能够验证取得的区间是否满足要求。

但当第3、4个指针指向同一个内存地址时,由于C/C++语言的参数压栈顺序是自右向左,于是该指针指向的值最后是等于减小的值,也就是legacy code中两次对Reader.Invert(…)的调用反而得到了一个左移的区间,这和Reader.Invert(…)代码实现的方式不同,导致为了加入单元测试,必须实现新的functor。

由此看到,为了使程序变得testable,即使是通过多个指针向外传值也最好和C/C++语言参数压栈的方向相同,否则维护它们的人要想不改变原有逻辑就要哭爹喊娘咯。

PS. 有谁知道WordPress该如何显示格式化的代码?在WordPress.com的免费帐户不能用吗?

Tags:

Follow

Get every new post delivered to your Inbox.