我们的代码(在一个简单的库实现中)开始看起来像这样:
err = callToUnderlyingLibrary1();
if (err!=0) {
printf ("blah %d\n", err);
...
}
err = callToUnderlyingLibrary2();
if (err!=0) {
printf ("blah %d\n", err);
...
}
err = callToUnderlyingLibrary3();
if (err!=0) {
printf ("blah %d\n", err);
...
}
这很麻烦,很难看。有一个更好的方法吗 ?也许使用C预处理器?我想的是:
CHECK callToUnderlyingLibrary1();
CHECK callToUnderlyingLibrary2();
CHECK callToUnderlyingLibrary3();
其中CHECK宏调用函数并进行基本错误检查。
是否有首选的惯用方法来处理这个问题?
答案 0 :(得分:15)
通常,在C中,使用goto
进行错误处理:
int foo()
{
if (Function1() == ERROR_CODE) goto error;
...
struct bar *x = acquire_structure;
...
if (Function2() == ERROR_CODE) goto error0;
...
release_structure(x);
return 0;
error0:
release_structure(x);
error:
return -1;
}
这可以通过宏和更聪明的指令流来改进(以避免重复清理代码),但我希望你明白这一点。
答案 1 :(得分:13)
另一种基于宏的方法,您可以使用它来轻松缓解C中的缺点:
#define CHECK(x) do { \
int retval = (x); \
if (retval != 0) { \
fprintf(stderr, "Runtime error: %s returned %d at %s:%d", #x, retval, __FILE__, __LINE__); \
return /* or throw or whatever */; \
} \
} while (0)
然后调用它,你有:
CHECK(doSomething1());
CHECK(doSomething2());
// etc.
对于奖励积分,您可以轻松扩展CHECK宏以获取第二个参数y,即失败时该怎么做:
#define CHECK(x, y) do { \
int retval = (x); \
if (retval != 0) { \
fprintf(stderr, "Runtime error: %s returned %d at %s:%d", #x, retval, __FILE__, __LINE__); \
y; \
} \
} while (0)
// We're returning a different error code
CHECK(someFunction1(foo), return someErrorCode);
// We're actually calling it from C++ and can throw an exception
CHECK(someFunction2(foo), throw SomeException("someFunction2 failed")):
答案 2 :(得分:6)
我认为您应该查看异常和异常处理。 http://www.cplusplus.com/doc/tutorial/exceptions/
try{
callToUnderlyingLibrary1();
callToUnderlyingLibrary2();
callToUnderlyingLibrary3();
}catch(exception& e)
//Handle exception
}
如果出现错误,您的库函数可能会抛出异常
答案 3 :(得分:3)
这是一个命题,你可能喜欢也可能不喜欢它:
errno
)die()
函数,根据错误代码(或任何你想要它做的事情)打印错误。do_something(foo, bar) || die("Argh...");
答案 4 :(得分:1)
你可以做你所说的,这是一些基本的宏观:
#define CHECK(x) (err = x()); \
if (err) { \
printf("blah %d on line %d of file %s\n", err, __LINE__, __FILE__); \
} \
else (void)0
你可以像
一样使用它int err = 0;
CHECK(callToUnderlyingLibrary1); // don't forget the semicolon at the end
CHECK(callToUnderlyingLibrary2);
CHECK(callToUnderlyingLibrary3);
答案 5 :(得分:1)
我更喜欢Alexandra C.的goto-approach的变体:
int foo()
{
int rv = 0;
struct bar *x = NULL;
struct bar *y = NULL;
rv = Function1();
if (rv != OK){
goto error;
}
//...
x = acquire_structure();
if (x==NULL){
rv = ERROR_MEMORY;
goto error;
}
//...
rv = Function2();
if (rv != OK){
goto error;
}
//...
y = acquire_structure();
if (y==NULL){
rv = ERROR_MEMORY;
goto error;
}
//...
rv = release_structure(x);
x = NULL;
if (rv != OK){
goto error;
}
rv = release_structure(y);
y = NULL;
if (rv != OK){
goto error;
}
return OK;
error:
if (x!=NULL){
release_structure(x);
}
return rv;
}
当您使用多个goto-destination时,很容易将它们混合起来。或者您可能会移动变量的初始化,但忘记更新gotos。并且很难测试所有 C方法失败的方式。
我更喜欢有一个执行所有清理的goto-destination。我发现这样可以更容易避免错误。
答案 6 :(得分:-4)
没有'转到',在函数中只使用1'返回'。这是优雅的代码。
恕我直言,OP的问题点和所有答案都在谈论FANCY技术。花哨的代码只是一种眼睛糖果。