更新的解决方案
如果您没有errorCode
定义(或不总是希望它设置),请改用它:
#define CALL_AND_LOG_ERROR(callee) \
CheckCallAndPrintError(callee, #callee, __func__, __LINE__, __FILE__)
int CheckCallAndPrintError( int result, const char* callee,
const char* caller, int line, const char* file)
{
if(result)
{
fprintf(stderr, "Error %d returned by function %s called from function %s on line \
%d of file %s\n", result, callee, caller, line, file);
}
return result;
}
然后你可以CALL_AND_LOG_ERROR
像下面这样使用,除了如果你想保留这个值,你必须明确地分配给一个变量:
int errorCode = CALL_AND_LOG_ERROR(SomeFunc(a, 0, NULL));
虽然这解决了对外部变量的依赖,但我不知道优化器多久内联一次调用。我怀疑无论哪种方式对性能的影响都会很大。请注意,两种解决方案都假定 API 返回值为int
s。如果它们是其他一些标量类型(或不同函数的不同标量类型),您可能必须更改它们的传递/输出方式。
原始解决方案
调用一个调用你的函数的宏绝对是这里的方法,但我建议使用三元运算符而不是控制结构。这允许您使用宏:
- 作为一个独立的声明(
errorCode
如果你愿意,可以稍后检查)
- 作为控制结构的控制语句
- 在一个表达式中
- 作为函数的操作数
示例代码(您可以在http://ideone.com/NG8U16使用它):
#include <stdio.h>
// these macros rely on the fact that errorCode is defined in the the caller
#define FUNC_RETURN_ISNT(func, unexpected) \
((errorCode = func) == unexpected) ? printf("error: call %s shouldn't have returned %d but did (from function %s at line %d of file %s)\n", #func, unexpected, __func__, __LINE__, __FILE__), 0 : 1
#define FUNC_RETURN_IS(func, expected) \
((errorCode = func) != expected) ? printf("error: call %s returned %d instead of %d (from function %s at line %d of file %s)\n", #func, errorCode, expected, __func__, __LINE__, __FILE__), 0 : 1
#define ERROR_CHECK(func) \
(errorCode = func) ? printf("error: call %s returned %d (from function %s at line %d of file %s)\n", #func, errorCode, __func__, __LINE__, __FILE__), errorCode : 0
int func(int a, int b, int c)
{
return a^b^c;
}
int func2(void)
{
return -1;
}
int func3(void)
{
static int i = 3;
return i--;
}
int main(void)
{
int a = 0, b = 0, c = 0;
int errorCode;
int (*funcPoint)(void) = func2;
FUNC_RETURN_ISNT(func(1,1,1), 1);
FUNC_RETURN_IS(func(a,b,c), 1);
ERROR_CHECK(func(a,b,1));
if(ERROR_CHECK(func2()))
{
printf("func2 failed error check\n");
}
else
{
printf("func2 passed error check\n");
}
if(ERROR_CHECK(funcPoint()))
{
printf("funcPoint failed error check\n");
}
else
{
printf("funcPoint passed error check\n");
}
if(ERROR_CHECK(func(0,0,0)))
{
printf("func failed error check\n");
}
else
{
printf("func passed error check\n");
}
while(ERROR_CHECK(func3()))
{
printf("retry...\n");
}
switch(ERROR_CHECK(func(1,2,4)))
{
case 0:
printf("okay\n");
break;
case 1:
printf("non-fatal error 1\n");
break;
case 7:
printf("fatal error 7\n");
return 1;
}
return 0;
}
示例输出:
error: call func(1,1,1) shouldn't have returned 1 but did (from function main at line 38 of file prog.c)
error: call func(a,b,c) returned 0 instead of 1 (from function main at line 40 of file prog.c)
error: call func(a,b,1) returned 1 (from function main at line 42 of file prog.c)
error: call func2() returned -1 (from function main at line 44 of file prog.c)
func2 failed error check
error: call funcPoint() returned -1 (from function main at line 53 of file prog.c)
funcPoint failed error check
func passed error check
error: call func3() returned 3 (from function main at line 71 of file prog.c)
retry...
error: call func3() returned 2 (from function main at line 71 of file prog.c)
retry...
error: call func3() returned 1 (from function main at line 71 of file prog.c)
retry...
error: call func(1,2,4) returned 7 (from function main at line 76 of file prog.c)
fatal error 7
但是,仍然存在一些缺点:
- 它并不完全漂亮。语法对我来说感觉很尴尬,它可能会违反一些编码标准。
- 应该很脆吧 我没有考虑太多,但我敢打赌你可以通过传递一些相对合理的东西来打破它。
- 它需要
errorCode
已经定义(并且由于 ,它必须是一个整数类型printf
)。然而,公平地说,原始代码片段也是如此。可以修改宏以接受变量作为参数(它不必总是被命名errorCode
,但仍然必须在宏之外定义)。
- 它打印用于调用该函数的确切代码。在某些(甚至大多数)情况下,这实际上可能是一件好事,但是我认为这在函数指针场景中并不理想(当然,在调用 API 时可能不常见)。此外,更难打印传递参数的实际值。这两种方法都可以在一定程度上解决,但代价是让它更笨拙和/或更不便携。