27

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.

Richard Chambers
  • 16,643
  • 4
  • 81
  • 106
patros
  • 7,719
  • 3
  • 28
  • 37

20 Answers20

54

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.

Eclipse
  • 44,851
  • 20
  • 112
  • 171
47

The second snippet just looks wrong. You're effectively re-invented goto.

Anyone reading the first code style will immediately know what's happening, the second style requires more examination, thus makes maintenance harder in the long run, for no real benefit.

Edit, in the second style, you've thrown away the error code, so you can't take any corrective action or display an informative message, log something useful etc....

Glen
  • 21,816
  • 3
  • 61
  • 76
  • 9
    No, goto is different. In this case he just jumps out of the current scope and thus nothing can go wrong. – Tamara Wijsman Sep 11 '09 at 16:59
  • 9
    @Glen: Nope, not harsh at all. That's the first thing I thought of too. +1 – Graeme Perrow Sep 11 '09 at 17:03
  • @TomWij: remove `do`. Replace `while(false)` with `error:`. Replace `break` with `goto error`. Same behavior. – Shog9 Sep 11 '09 at 17:04
  • 1
    Even if it where goto - sometimes a central point for error handling will keep your code much cleaner. The secret is to know when to use which method. Linux (kernel sources) for example use goto a lot. – Shirkrin Sep 11 '09 at 17:05
  • 1
    I presume he meant to type: if ((errorCode = doSomething()) != 0) break; and then have: if (errorCode > 0) handleError(errorCode); after everything. Otherwise he's ignoring the errorCode which makes debugging much more hassle than seeing a log/console message, as you say. – JeeBee Sep 11 '09 at 17:05
  • 1
    using goto clean_up to clean up following an error before returning isn't that uncommon in C. – Pete Kirkham Sep 11 '09 at 17:07
  • 36
    @Shirkrin: if you want goto, then **use goto** - don't mislead the reader by implying there's a loop when there isn't one. – Shog9 Sep 11 '09 at 17:09
  • I agree with Glen. It is nothing but a glorified goto. – Naveen Sep 11 '09 at 17:16
  • 2
    yep, it's a goto. HOWEVER, I have had one instance where a GOTO was the right solution. (One. In like eight years.) This, however, is not it. – John Gietzen Sep 11 '09 at 17:31
  • Yes, edited the question. If the error code contains useful information, I will keep it. Often it's just an indication that information has been put somewhere else (errno). – patros Sep 11 '09 at 18:16
  • 10
    Goto? By the same logic, "if", "for", and function calls are also "glorified goto". We add restrictions to full-blown "goto" to make it more usable -- "break" is one such restriction. The "do" might be misleading (a loop?), but "goto" is also misleading (jump to anywhere?). The "break" has specific meaning: escape one level of context. – Ken Sep 11 '09 at 19:04
  • 2
    So maybe the answer is "yes", you're the only one who does that – LB40 Sep 11 '09 at 19:31
  • This is certainly not reinventing goto in any way. In terms of control-flow, this is perfectly safe (it has more in common with `return` than `goto`. Is `return` evil too?) Of course, in terms of readability, I prefer the first version. – jalf Apr 07 '10 at 15:20
  • The goto issue is a side issue. The main problem is the loss of the error code information. THAT's the reason to vote up this answer. – srm Jun 18 '15 at 18:41
17

The first style is a pattern the experienced eye groks at once.

The second requires more thought - you look at it and see a loop. You expect several iterations, but as you read through it, this mental model gets shattered...

Sure, it may work, but programming languages aren't just a way to tell a computer what to do, they are a way to communicate those ideas to other humans too.

Paul Dixon
  • 295,876
  • 54
  • 310
  • 348
  • 1
    It's not an infinite loop. That's part of the weird. It's a do while loop that will only execute once. He's using breaks to skip over methods he doesn't want to execute. Of course, the fact that you did miss this subtlety is good reason to not use it. If reasonably experienced developers can miss this, then what hope do less experienced ones have. – Benjamin Autin Sep 11 '09 at 17:00
  • Not trying to imply anything bad about you. Just trying to illustrate why we should keep obscure coding tricks to a minimum. – Benjamin Autin Sep 11 '09 at 17:01
  • I'd corrected that before you wrote the comment, but you're right, it only serves to illustrate the sort of cognitive dissonance the second pattern causes! – Paul Dixon Sep 11 '09 at 17:10
  • 1
    I don't think you ever want to set up expectations in code and then break them some time later. Any use of "do {} while (false);" should be flagged up front in a comment, or, preferably, transformed into something else. – David Thornley Sep 11 '09 at 17:13
12

I think the first one gives you more control over what to do with a particular error. The second way only tells you that an error occurred, not where or what it was.

Of course, exceptions are superior to both...

rlbond
  • 65,341
  • 56
  • 178
  • 228
  • Using exceptions would be great. But we still have to use code that doesn't use exceptions, e.g system API's, dlclose etc. So having a good system for dealing with these error codes is important – Glen Sep 11 '09 at 17:02
  • 3
    Joel Spolsky's complaint that exceptions are invisible goto's is here: http://www.joelonsoftware.com/articles/Wrong.html – Glenn Sep 11 '09 at 17:02
  • but imagine.. there are no exceptions if you wan't to write c?! – Shirkrin Sep 11 '09 at 17:06
  • @Shirkrin - You can have "exceptions", you just have to use gotos to simulate them. See Eclipse's answer. And then see Joel Spolsky's argument against using exceptions in the first place, as referenced by Glenn. – Chris Lutz Sep 11 '09 at 17:12
  • 1
    I hate exceptions. Good solid return codes. Some of us out here *DO* check them you know. – Chris K Sep 11 '09 at 17:26
8

Make it short, compact, and easy to quickly read?

How about:

if ((errorcode = doSomething()) == 0
&&  (errorcode = doSomethingElse()) == 0
&&  (errorcode = doSomethingElseNew()) == 0)
    maybe_something_here;
return errorcode; // or whatever is next
DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
  • 1
    Maybe not the easiest thing to read, but the built-in short-circuiting behaviour is exactly the desired one. – Eclipse Sep 11 '09 at 17:48
4

Why not replace the do/while and break with a function and returns instead?

You have reinvented goto.

  • I guess since this isn't C++ one might need to do manual cleanup before return. – Eugene Sep 11 '09 at 17:59
  • There may be code that gets executed after the "loop". The "loop" could be made into it's own functions, with returns instead of breaks though. – patros Sep 11 '09 at 18:21
3

What about using exceptions?

try {
  DoSomeThing();
  DoSomethingElse();
  DoSomethingNew();
  .
  .
  .
}
catch(DoSomethingException e) {
  .
  .
}
catch(DoSomethingElseException e) {
  .
  .
}
catch(DoSomethingNewException e) {
  .
  .
}
catch(...) {
  .
  .
}
Traveling Tech Guy
  • 27,194
  • 23
  • 111
  • 159
  • 1
    which would be great, if every method we called threw instead of returning an exception. However we don't have control over every method we call. most system API's use errorCode's instead of exceptions – Glen Sep 11 '09 at 17:04
  • 4
    @Glen: `if ( !(error = FunctionCall()) ) throw CustomException(error);` – Shog9 Sep 11 '09 at 17:08
  • 2
    @Paul: Exceptions are more like COMEFROM, actually. – David Thornley Sep 11 '09 at 17:09
  • @David: Because that's even better. ;-) – Paul Nathan Sep 11 '09 at 17:16
  • @Paul - I agree this is the closest you get to GOTO without using the G-word, but as someone who developed natural language compilers - sometimes GOTO is the fastest, easiest, clearest way to get what you need. Being scoffed at by the academy doesn't make it less useful :) @Shog9 - great solution! – Traveling Tech Guy Sep 11 '09 at 17:19
  • @TTG: I'm not arguing that GOTO is _sometimes_ the cleanest way. I do think exceptions are effectively another "action at a distance" effect much like GOTO. I've typically found return-codes to be a mentally cleaner way to manage these kinds of issues than exception-spaghetti. – Paul Nathan Sep 11 '09 at 19:59
3

Your method isn't really bad and it's not unreadable like people here are claiming, but it is unconventional and will annoy some (as you noticed here).

The first one can get REALLY annoying after your code gets to a certain size because it has a lot of boilerplate.

The pattern I tended to use when I couldn't use exceptions was more like:

fn() {
    while(true) {
        if(doIt())
            handleError();//error detected...
    }
}

bool doIt() {
    if(!doThing1Succeeds())
        return true;
    if(!doThing2Succeeds())
        return true;
    return false;
}

Your second function should be inlined into the first if you put the correct magic incantations in the signature, and each function should be more readable.

This is functionally identical to the while/bail loop without the unconventional syntax (and also a bit easier to understand because you separate out the concerns of looping/error handling from the concerns of "what does your program do in a given loop".

Bill K
  • 62,186
  • 18
  • 105
  • 157
2

This should be done through exceptions, at least if the C++ tag is correct. There is nothing wrong if you are using C only, although I suggest to use a Boolean instead as you are not using the returned error code. You don't have to type != 0 either then...

Tamara Wijsman
  • 12,198
  • 8
  • 53
  • 82
  • which would be great, if every method we called in C++ threw instead of returning an exception. However we don't have control over every method we call. most system API's use errorCode's instead of exceptions – Glen Sep 11 '09 at 17:06
1

I've used the technique in a few places (so you aren't the only one who does it). However, I don't do it as a general rule, and I have mixed feelings about it where I have used it. Used with careful documentation (comments) in a few places, I'm OK with it. Used everywhere - no, generally not a good idea.

Relevant exhibits: files sqlstmt.ec, upload.ec, reload.ec from SQLCMD source code (not, not Microsoft's impostor; mine). The '.ec' extension means that the file contains ESQL/C - Embedded SQL in C which is pre-processed to plain C; you don't need to know ESQL/C to see the loop structures. The loops are all labelled with:

    /* This is a one-cycle loop that simplifies error handling */
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
1

The classic C idiom is:

if( (error_val = doSomething()) == 0)
{ 
   //Manage error condition
}

Note that C returns the assigned value from an assignment, enabling a test to be performed. Often people will write:

if( ! ( error_val = doSomething()))

but I retained the == 0 for clarity.

Regarding your idioms...

Your first idiom is ok. Your second idiom is an abuse of the language and you should avoid it.

Paul Nathan
  • 39,638
  • 28
  • 112
  • 212
  • 1
    I personally prefer to have the assignment and the checking on different lines, because it starts to look cluttered if I don't (probably because I prefer not to have spaces between parenthesis. I prefer not to have more than one pair of parenthesis regardless). – Chris Lutz Sep 11 '09 at 18:24
  • I'm not sayin' that this is the One True Way. This is just the classic idiom. I usually split the assignment and checking into two blocks as well - seems to work better as code evolves. – Paul Nathan Sep 11 '09 at 20:00
1

How about this version then

I'd usually just do something like your first example or possibly with a boolean like this:

bool statusOkay = true;

if (statusOkay)
    statusOkay = (doSomething() == 0);

if (statusOkay)
    statusOkay = (doSomethingElse() == 0);

if (statusOkay)
    statusOkay = (doSomethingElseNew() == 0);

But if you are really keen on the terseness of your second technique then you could consider this approach:

bool statusOkay = true;

statusOkay = statusOkay && (doSomething() == 0);
statusOkay = statusOkay && (doSomethingElse() == 0);
statusOkay = statusOkay && (doSomethingElseNew() == 0);

Just don't expect the maintenance programmers to thank you!

GrahamS
  • 9,980
  • 9
  • 49
  • 63
1

I use the do { } while (false); every once in a while when it seems appropriate. I see it as being something like a try/catch block in that I have code that is setup as a block with a series of decisions with possible exceptions and the need is to have the various paths through the rules and logic to merge at the end of the block.

I am pretty sure I only use this construct with C programming and it is not very often.

With the specific example you gave of a series of function calls that will be performed one after the other with the complete series being done or the series stopped if an error is detected, I would probably just use if statements checking an error variable.

{
    int iCallStatus = 0;
    iCallStatus = doFunc1();
    if (iCallStatus == 0) iCallStatus = doFunc2();
    if (iCallStatus == 0) icallStatus = doFunc3();
}

This is short and the meaning is straightforward and clear even without comments.

What I have run into from time to time is that this fairly straightforward sequential flow of procedural steps does not apply to a particular requirement. What I need is to create a code block with various decisions, usually involving loops or iterating over some series of data objects and I want to treat this series as a kind of transaction in which the transaction will be committed if there is no error or aborted if there is some kind of an error condition found during the processing of the transaction. As part of this data block, I may create a set of temporary variables for the scope of the do { } while (false); When ever I use this, I always put a comment indicating that this is a single iteration do while, something like:

do {  // single loop code block begins
    // block of statements for business logic with single ending point
} while (false);  // single loop code block ends

When ever I find myself thinking this construct is necessary, I look to see if the code needs to be refactored or if a function or set of functions would be more appropriate.

The reason I prefer this construct over using a goto statement is that the use of brackets and indentation makes the source code easier to read. With my editor I can find the top and bottom of the block easily and the indentation makes it easier to visualize the code as a block with a single entry point and a known ending point. There may be multiple exit points within the block however I do know where they will all end up. Using this means that I can create localized variables that will go out of scope though just using brackets without the do { } while (false); does that as well. However I use the do while because I need the break; capability as well.

I would consider using this style under some of the following conditions. If the business logic that is being implemented requires a set of variables that are shared and referenced by different possible execution pathways which rejoin. If the business logic is complex with multiple states and checks with several levels of if statements and if an error is detected during the processing, an indication to the error is set and the processing is aborted.

The only times that I can think of when I have used this is with something a bit gnarly and this helped to clarify and make the processing abort easier. So basically I was using this similar to throwing an exception with a try/catch.

Richard Chambers
  • 16,643
  • 4
  • 81
  • 106
  • The fact that you need comments should be an immediate red flag that the code will be hard to maintain. – Alex Pana Jan 12 '15 at 13:54
0

The first style is a good example of why exceptions are superior: You can't even see the algorithm because it's buried under explicit error handling.

The second style abuses a loop construct to mimic goto in a mislead attempt to avoid having to explicitly spell out goto. It's plainly evil and long-time usage will lead you to the dark side.

sbi
  • 219,715
  • 46
  • 258
  • 445
0

For me, I'd prefer:

if(!doSomething()) {
    doSomethingElse();
}
doSomethingNew();

All of the other stuff is syntactic noise that is obscuring the three function calls. Inside of Else and New you can throw an error, or if older, use longjmp to go back to some earlier handling. Nice, clean and rather obvious.

Paul W Homer
  • 2,728
  • 1
  • 19
  • 25
0

There seems to be a deeper problem here than your control constructs. Why do you have such complex error control? I.e. you seem to have multiple ways of handling different errors.

Typically, when I get an error, I simply break off the operation, present an error message to the user, and return control to the event loop, if an interactive application. For batch, log the error, and either continue processing on the next data item or abort the processing.

This kind of control flow is easily handled with exceptions.

If you have to handle error numbers, then you can effectively simulate exceptions by continuing normal error processing in case of an error, or returning the first error. Your continued processing after an error occurs seems to be very fragile, or your error conditions are really control conditions not error conditions.

Larry Watanabe
  • 10,126
  • 9
  • 43
  • 46
0

Honestly the more effective C/C++ programmers I've known would just use a gotos in such conditions. The general approach is to have a single exit label with all cleanup after it. Have only one return path from the function. When the cleanup logic starts to get complicated/have conditionals, then break the function into subfunctions. This is pretty typical for systems coding in C/C++ imo, where the APIs you call return error codes rather than throw exceptions.

In general, gotos are bad. Since the usage I've described is so common, done consistently I think its fine.

Frank Schwieterman
  • 24,142
  • 15
  • 92
  • 130
0

The second style is commonly used for managing resource allocations and de-allocations in C, where RAII doesn't come to the rescue. Typically, you would declare some resources before the do, allocate and use them inside the pseudo-loop, and de-allocate them outside.

An example of the general paradigm is as follows:

int status = 0;

// declare and initialize resources
BYTE *memory = NULL;
HANDLE file = INVALID_HANDLE_VALUE;
// etc...

do
{
  // do some work

  // allocate some resources
  file = CreateFile(...);
  if(file == INVALID_HANDLE_VALUE)
  {
    status = GetLastError();
    break;
  }

  // do some work with new resources

  // allocate more resources

  memory = malloc(...);
  if(memory == NULL)
  {
    status = ERROR_OUTOFMEMORY;
    break;
  }

  // do more work with new resources
} while(0);

// clean up the declared resources
if(file != INVALID_HANDLE_VALUE)
  CloseHandle(file);

if(memory != NULL)
  free(memory);

return status;

Having said that, RAII solves the same problem with much cleaner syntax (basically, you can forget the cleanup code altogether) and handles some scenarios that this approach does not, such as exceptions.

deGoot
  • 996
  • 4
  • 11
0

I've seen this pattern before and didn't like it. Usually, it could be cleaned up by pulling the logic into a separate function.

The code then becomes

    ...
    int errorCode = doItAll();
    ...


    int doItAll(void) {
      int errorCode;
      if(errorCode=doSomething()!=0)
        return errorCode;
      if(errorCode=doSomethingElse()!=0)
        return errorCode;
      if(errorCode=doSomethingElseNew()!=0)
        return errorCode;
      return 0;
    }

Combining this with cleanup becomes pretty easy too, you just use a goto and error handler like in eclipses answer.

    ...
    int errorCode = doItAll();
    ...


    int doItAll(void) {
      int errorCode;
      void * aResource = NULL; // Somthing that needs cleanup after doSomethingElse has been called
      if(errorCode=doSomething()!=0) //Doesn't need cleanup
        return errorCode;
      if(errorCode=doSomethingElse(&aResource)!=0)
        goto cleanup;
      if(errorCode=doSomethingElseNew()!=0)
        goto cleanup;
      return 0;
    cleanup:
      releaseResource(aResource);
      return errorCode;
    }
Michael Anderson
  • 70,661
  • 7
  • 134
  • 187
-1

I use the second approach when I am managing allot of pointers and when the function is acceptable to fail that throwing an exception is not really the correct answer.

I find it is easier to manage cleanup of pointers in one place instead of in several places and also you know that it is only going to return in one place.


pointerA * pa = NULL;
pointerB * pb = NULL;
pointerB * pc = NULL;
BOOL bRet = FALSE;
pa = new pointerA();
do {
  if (!dosomethingWithPA( pa ))
    break;
   pb = new poninterB();
  if(!dosomethingWithPB( pb ))
    break;
  pc = new pointerC();
  if(!dosemethingWithPC( pc ))
    break;
  bRet = TRUE;
} while (FALSE);

//
// cleanup
//
if (NULL != pa)
 delete pa;
if (NULL != pb)
 delete pb;
if (NULL != pc)
 delete pc;

return bRet;

in contrast with


pointerA * pa = NULL;
pointerB * pb = NULL;
pointerB * pc = NULL;

pa = new pointerA(); if (!dosomethingWithPA( pa )) { delete pa; return FALSE; }

pb = new poninterB(); if(!dosomethingWithPB( pb )) { delete pa; delete pb; return FALSE; } pc = new pointerC(); if(!dosemethingWithPAPBPC( pa,pb,pc )) { delete pa; delete pb; delete pc; return FALSE; }

delete pa; delete pb; delete pc; return TRUE;

anon
  • 1