我正在开发一个C数学库,它正在使用宏来完成大部分工作,我现在面临一个问题。
这就是宏的样子:
the_macro(a, b, c)
并且宏本身就是这样的:
(a - b > 0) ? error_function : 1
error_function用于在编译时停止用户,因此如果(a - b > 0)
为true
,则宏将作为没有定义的函数进行扩展。所以这会导致链接错误。
Everthing似乎很好,但今天我的老板告诉我我们需要做一些单元测试,所以我写了一个包装宏的函数:
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
答案 0 :(得分:4)
无论您在何处使用宏,如果未声明error_function
,您都应该收到编译器错误。如果它已声明但未定义,则表示您具有未定义的行为。宏的参数是否是常量在这方面没有任何改变。 (它可能会影响未定义行为的实际行为。)
答案 1 :(得分:2)
当您使用常量调用宏时,编译器会知道该值,因此,可能与优化一样,表达式the_macro (5, 4, 0)
将替换为1
而不是error_function
。当表达式a-b
求值为<= 0
时,编译器会将其替换为error_function
,并停止编译。
另一方面,当你使用变量时,编译器不知道表达式的结果,并且必须使用宏的完全扩展,其中包含对未定义函数的调用,因此你得到了链接错误
答案 2 :(得分:1)
出于单元测试的目的(仅),为什么不将error_function()
定义为单元测试的一部分,并让它无条件地返回错误,您的测试框架可以检测到。这样你就可以使用常量或变量来模拟你在编译时看到的行为。
它不是完全你想要的东西,但是单元测试框架总是按照它们本质的运行时测试机制,因此可能无法实现自动编译时测试。
或者,您可以使用system()
运行包含库的命令行构建,将输出(包括错误)重定向到文件中。然后,您可以打开文件并扫描链接错误的已知文本。
答案 3 :(得分:1)
让我们看看我是否理解这一点:
如果a-b>0
,您想要一种破解编译的方法吗?除非你使用C11,否则这实际上是不可能的。根据条件,根本没有办法让编译器中止。在您的情况下,您尝试使用优化程序和链接器的组合来获得所需的行为。但这无法可靠地运作。
如果a-b> 0,优化程序可以将(a - b > 0) ? error_function : 1
表达式减少为1,但这不能保证。编译器必须显示由C标准定义的保证行为,并且该标准未提及优化器。相同的优化器有时可能会减少表达式,有时不会根据代码中的其他内容减少它。或者它可能会或可能不会减少它,具体取决于您传递的命令行标志。
因此,使用此宏时,您正在编写代码,当您切换编译器,编译器版本,操作系统,添加或删除链接库或目标体系结构时,代码可能会突然意外中断。根据这些变化突然中断的代码非常糟糕。不要对你的开发人员这样做。
最好编写可移植代码,您可以确定未来的编译器会理解它,因为它符合标准。在C11之前,没有办法做到这一点。如果你确实需要这个,请告诉你的老板唯一的方法是使用带有static_assert
关键字的C11,它可以让你有条件地堕胎。