0

我正在研究一个 C 数学库,它使用宏来完成大部分工作,我现在面临一个问题。

这是宏的样子:

the_macro(a, b, c)

宏本身会执行以下操作: (a - b > 0) ? error_function : 1

error_function 用于在编译时停止用户,因此如果(a - b > 0)is true,则宏将扩展为没有定义的函数。所以这会导致链接错误。

一切似乎都很好,但今天我的老板告诉我我们需要做一些单元测试,所以我写了一个包装宏的函数:

int my_func(int a, int b, int c)
{
    return the_macro(a, b, c);
}

问题来了,代码不能传递链接,因为如果我使用var而不是常量来调用the_macro,这些error_functions都会在.o文件中,因为int a, int b, int c在运行时都是已知的,所以我只能调用宏函数带常量:the_macro(2, 3, 4)有什么办法可以避免这种情况?还是有更好的解决方案来对此宏进行单元测试?

编辑:

我正在处理的代码是机密的......但我做了一个例子来说明这个问题:

#include <stdio.h>

#define the_macro(a, b)\
    (a > b)?error_function():1

// Comment out my_func(), then the program will run normaly
// But if you don't comment it out, the linkage error will come out.
void my_func(int a, int b)
{
    the_macro(a, b);
}

int main()
{
    printf("%d\n", the_macro(1, 10));
    return 0;
}

我正在使用 gcc-4

4

4 回答 4

4

不管你在哪里使用宏,如果error_function没有声明,你应该得到一个编译器错误。如果它已声明但未定义,则您有未定义的行为。宏的参数是否为常量在这方面没有任何改变。(在未定义行为的情况下,它可能会影响实际行为。)

于 2012-06-08T07:48:25.363 回答
2

当您使用常量调用宏时,编译器知道该值,因此,也许作为优化,表达式the_macro (5, 4, 0)被替换为1而不是error_function. 当您的表达式a-b计算结果为 时<= 0,您的编译器会将其替换为error_function,并停止编译。

另一方面,当您使用变量时,编译器不知道表达式的结果,并且必须使用宏的完整扩展,其中包含对未定义函数的调用,因此会出现链接错误。

于 2012-06-08T07:46:02.863 回答
1

出于您单元测试的目的(仅)为什么不将其定义error_function()为单元测试的一部分并让它无条件地返回您的测试框架可以检测到的错误。这样,您应该能够使用常量或变量来模仿您在编译时看到的行为。

这并不完全是您想要的,但单元测试框架本质上总是运行时测试机制,因此自动编译时测试可能是不可能的。

或者,您可以使用system()运行包含库的命令行构建,将输出(包括错误)重定向到文件中。然后,您可以打开文件并扫描链接错误的已知文本。

于 2012-06-08T07:47:04.257 回答
1

让我们看看我是否理解正确:

你想要一种打破编译的方法a-b>0吗?除非您使用 C11,否则这实际上是不可能的。根本没有办法让编译器根据条件中止。在您的情况下,您正在尝试使用优化器和链接器的组合来获得所需的行为。但这不能可靠地工作。

(a - b > 0) ? error_function : 1 如果 ab>0,优化器可能会将表达式减少为 1,但这不能保证。有一个保证行为编译器必须显示由 C 标准定义,并且该标准没有提到优化器。相同的优化器有时可能会减少表达式,有时不会减少它,具体取决于代码中的其他内容。或者它可能会或可能不会减少它,具体取决于您传递的命令行标志。

因此,使用此宏您正在编写代码,当您切换编译器、编译器版本、操作系统、添加或删除链接库或目标体系结构时,可能会突然意外中断。因此类更改而突然中断的代码非常糟糕。不要对你的开发人员这样做。

最好编写可移植的代码,因为它遵循标准,因此您可以确定未来的编译器会理解它。在 C11 之前,没有办法做到这一点。如果你真的需要这个,告诉你的老板唯一的方法是使用 C11,它有一个static_assert关键字可以让你有条件地中止编译。

于 2012-06-08T07:59:39.447 回答