A while back I switched the way I handled c style errors.
I found a lot of my code looked like this:
int errorCode = 0; errorCode = doSomething(); if (errorCode == 0) { errorCode = doSomethingElse(); } ... if (errorCode == 0) { errorCode = doSomethingElseNew(); }
But recently I've been writing it like this:
int errorCode = 0; do { if (doSomething() != 0) break; if (doSomethingElse() != 0) break; ... if (doSomethingElseNew() != 0) break; } while(false);
I've seen a lot of code where nothing gets executed after there's an error, but it has always been written in the first style. Is there anyone else who uses this style, and if you don't, why?
Edit: just to clarify, usually this construct uses errno
otherwise I will assign the value to an int
before breaking. Also there's usually more code than just a single function call within the if (error == 0 )
clauses. Lots of good points to think on, though.
The do while construct consists of a process symbol and a condition. First, the code within the block is executed, and then the condition is evaluated. If the condition is true the code within the block is executed again. This repeats until the condition becomes false.
The do while loop checks the condition at the end of the loop. This means that the statements inside the loop body will be executed at least once even if the condition is never true. The do while loop is an exit controlled loop, where even if the test condition is false, the loop body will be executed at least once.
Avoiding while loops is one. His rule for for loops is that you use them when there is a list (or tuple or generator) of elements to iterate over or a fixed number of iterations. This is correct. When there is not, and you instead have a certain state or condition you want to reach, you use while loops.
while loops use only Boolean expression and when it is true. So when it gets true it'll execute until it gets false. while(false) means the condition is false which will end the loop. while(True) means the condition is True which will continue the loop.
If you're using C++, just use exceptions. If you're using C, the first style works great. But if you really do want the second style, just use gotos - this is exactly the type of situation where gotos really are the clearest construct.
int errorCode = 0; if ((errorCode = doSomething()) != 0) goto errorHandler; if ((errorCode = doSomethingElse()) != 0) goto errorHandler; ... if ((errorCode = doSomethingElseNew()) != 0) goto errorHandler; return; errorHandler: // handle error
Yes gotos can be bad, and exceptions, or explicit error handling after each call may be better, but gotos are much better than co-opting another construct to try and simulate them poorly. Using gotos also makes it trivial to add another error handler for a specific error:
int errorCode = 0; if ((errorCode = doSomething()) != 0) goto errorHandler; if ((errorCode = doSomethingElse()) != 0) goto errorHandler; ... if ((errorCode = doSomethingElseNew()) != 0) goto errorHandlerSomethingElseNew; return; errorHandler: // handle error return; errorHandlerSomethingElseNew: // handle error return;
Or if the error handling is more of the "unrolling/cleaning up what you've done" variety, you can structure it like this:
int errorCode = 0; if ((errorCode = doSomething()) != 0) goto errorHandler; if ((errorCode = doSomethingElse()) != 0) goto errorHandler1; ... if ((errorCode = doSomethingElseNew()) != 0) goto errorHandler2; errorHandler2: // clean up after doSomethingElseNew errorHandler1: // clean up after doSomethingElse errorHandler: // clean up after doSomething return errorCode;
This idiom gives you the advantage of not repeating your cleanup code (of course, if you're using C++, RAII will cover the cleanup code even more cleanly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With