Elegant error checking
Our code (in a simple library implementation) is beginning to look like this:
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);
...
}
This is cumbersome and ugly. Is there a better way to do this ? Perhaps using the C preprocessor ? I was thinking something like:
CHECK callToUnderlyingLibrary1();
CHECK callToUnderlyingLibrary2();
CHECK callToUnderlyingLibrary3();
where the CHECK macro invokes the function and does the rudimentary error checking.
Are there pref开发者_如何学Pythonerred idiomatic ways of handling this ?
Another macro-based approach which you can use to mitigate the shortcomings in C fairly easily:
#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)
Then to invoke it you have:
CHECK(doSomething1());
CHECK(doSomething2());
// etc.
For bonus points you could easily extend the CHECK macro to take a second argument y that is what to do on failure:
#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")):
Usually, in C, one uses goto
for error handling:
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;
}
This can be improved with macros and more clever instruction flow (to avoid repeating cleanup code), but I hope you see the point.
I think you should look at exceptions and exception handling. http://www.cplusplus.com/doc/tutorial/exceptions/
try{
callToUnderlyingLibrary1();
callToUnderlyingLibrary2();
callToUnderlyingLibrary3();
}catch(exception& e)
//Handle exception
}
your library functions can throw exceptions if there is an error
Here is a proposition, you may or may not like it:
- make your functions return 0 on failure, something else on success
- if something fails in your functions, have them set a global (or static) variable to the error code (like
errno
) - create a
die()
function that prints the error depending of the error code (or whatever you want it to do) - call your functions with
do_something(foo, bar) || die("Argh...");
I prefer a variant of Alexandra C.'s 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;
}
When you use multiple goto-destinations, it is easy to mix them up. Or perhaps you move the initialization of a variable, but forget to update the gotos. And it can be very difficult to test all ways a C-method can fail.
I prefer having a single goto-destination that performs all the cleanup. I find that makes it easier to avoid mistakes.
You could do what you said, which is some rudimentary macro:
#define CHECK(x) (err = x()); \
if (err) { \
printf("blah %d on line %d of file %s\n", err, __LINE__, __FILE__); \
} \
else (void)0
And you could use it like
int err = 0;
CHECK(callToUnderlyingLibrary1); // don't forget the semicolon at the end
CHECK(callToUnderlyingLibrary2);
CHECK(callToUnderlyingLibrary3);
No 'goto', use only 1 'return' in functions. That's the elegant code.
IMHO, OP's question point and all answers are talking about FANCY techniques. Fancy code is just sort of eye candy.
精彩评论