views:

405

answers:

7

I was wondering if there was a better way of handling the case in C where you want to exit a function as soon as you encounter an error in a series of expressions. (in this case, its a function that returns a NULL on error)

e.g. in some C code where they tried to short circuit error handling by combining a series of statements with AND (&&).

return doSomething() && 
     doSomething2() && 
     doSomething3() && ... ;

This irritates me since we're cramming so much on one line in one statement. But I guess the alternative is

if (!(value = doSomething()))
    return NULL;
if (!(value = doSomething2()))
    return NULL;
etc
return value;

But what about short circuit error evaluation that I've seen in perl and bash scripts..

int die(int retvalue) {
    exit(retvalue);
}
.....
(value = doSomething()) || die(1);
(value = doSomething2()) || die(2);
(value = doSomething3()) || die(3);
return value;

The main problem with this is that the RHS has to be an expression so you can't really goto or return from the function. Would anyone find this of value or is it too limited?

edit: I guess I should have meant to include newlines in the first example. The problem is that you need to be careful if you decide to add another expression in the middle.

+3  A: 

If your only concern is cramming too much onto one line, why don't you just use:

return doSomething()
    && doSomething2()
    && doSomething3()
    && ... ;

I tend to write the second case as:

if (!(value = doSomething()))  return NULL;
if (!(value = doSomething2())) return NULL;
: : : : :
return value;

(even lining up the returns to make it readable) since I like to see as much code on a screen as once (and you can insert other checks in between the lines as needed).

paxdiablo
This helps readability, but it doesn't help the fact that the 'one line' is overloaded.
@devinb, I don't know why that fact is actually a problem. There's two things you need to be able to do with source (a) read and comprehend it, (b) compile it. The complier has no problems with either version so I assumed it was irritating because it was hard to read. Reformatting fixes that.
paxdiablo
As long as a solution gets compiled down to the same functionality and performance, they're all the same. Some, such as the goto-type solutions may be considered less readable (not by me since I think like a computer :-).
paxdiablo
I agree that the goto-solution is not perfect.However, there is a benefit to separating your function calls from your return statements and that is error handling. Whether they be GOTOs (not everyone's cup of tea) or by assert, or exit, or try_catch (it can be done in C) or by something else.
When you pile all those function calls (each with possible side effects) into one statement, you are limiting the ability and flexibility of your handlers to correctly assess what went wrong. Why is this so important? Because for 99.9% of your coding time, the program will NOT WORK.
Nick Sonneveld
There will always be bugs, so why make it harder to find them?
I like your second solution much better :)
NickS: obviously if the return values need to be checked against something else, or there's other checks, you would not do it in one statement. In that case I would simply use my second example as shown (with interspersed other checks as needed). It's all to do with readability of the code.
paxdiablo
A: 

I recommend against your proposed technique. At first, exit in C terminates the process; it not just returns. So it is limited to only few cases of fatal errors.

The first solution you are trying to avoid is the best and easiest understood in my point of view.

kgiannakakis
A: 

I posted an answer to this previous SO question which does something similar to what you need.

plinth
+3  A: 

I've seen the use of GOTOs in C for this exact purpose.

Because there isn't a 'finally' construct in C, a method was needed to free all your memory even if you were exiting a function early.

So it's essentially as follows (could someone correct my C syntax if I'm wrong, I'm a bit rusty)

int foo()
{
    /* Do Stuff */
    if(!DoSomething())
        GOTO Failure;

    if(!DoSomething2())
        GOTO Failure;

    if(!DoSomething3())
        GOTO Failure;

    /* return success */
    return true; 

    Failure:
    /* release allocated resources */
    /* print to log if necessary */
    return false;
}

Important note Do not use GOTOs for execution flow. They should only be used on error, and only to go to the end of the current function. If you use them for anything else, you're creating spaghetti code that could possibly destroy the fabric of reality. Just don't do it.

EDIT

As one of the posters noted, using Exit(x) will kill your entire program, which keeps that solution reserved for fatal errors. However your original proposed solution (DS() && DS2() && DS3()) all on one line poses a problem for error handling.

If you wanted to wrap the functions in some sort of function specific error handling, there is no way to do it when you wrap the function calls all in one line. So, at the very least you could do something like

int result1 = 0;
int result2 = 0;
int result3 = 0;

result1 = DoSomething();

if(result1)
    result2 = DoSomething2();

if(result2)
    result3 = DoSomething3();

return result1 && result2 && result3;

Because this method would not preclude error handling.

I'm not sure why. Your answer was quite reasonable.
Nick Sonneveld
Just a quick note, @devinb, your edit does all three somethings which is not the same as the short-circuited version in the question (or your original). Fine if there are no side effects (or performance is not affected) but that cannot be guaranteed.
paxdiablo
Thanks pax, I fixed it.
A: 

How about something like this?

int result;

result = doSomething();

result = result && doSomething2();

result = result && doSomething3();

return result;

This uses short-circuiting in a similar way to your first example, but it allows you to break it onto multiple lines and add comments etc.

GrahamS
GrahamS
Indeed. I apologize.
A: 

The idiomatic way of writing this in C is, in my experience, is to start the function with a series of if statements that check of pre-conditions of the rest of the function. If a pre-condition is not met, an immediate return with an error code is done. This way, when you get to the main part of the body of the function, you know that everything is OK and can keep the code as simple as possible.

In other words, you second approach.

As an example, one might write a function to copy a string as follows:

int copy_string(char *source, char *target, size_t max)
{
    if (source == NULL)
        return -1;
    if (target == NULL)
        return -1;
    source_len = strlen(source);
    if (source_len + 1 > max)   // +1 for NUL
        return -1;
    memcpy(target, source, source_len + 1);
    return 0;
}

(I like the Unix system call convention of returning -1 for errors, but that's a debatable matter of style and irrelevant to this question.)

Lars Wirzenius
A: 

I've seen this before:

int Function (void)
{
    int Result = DoSomething();

    if (Result) Result = DoSomething2();
    if (Result) Result = DoSomething3();
    if (Result) Result = DoSeomthing4();

    return Result;
}

It looks neat enough, easily alignable. It won't exit straight away but it won't execute anything else. This of course assumes that functions return non-zero on success. Otherwise you could simply use if (!Result) if functions return non-zero on failure.

dreamlax