C or C++ Return Status

16,076

Solution 1

We use this in C where I work:

int err = foo();
if (err) {
    // armageddon
}

The assignment and if could be combined, but with more complicated function calls it gets more confusing and some people are confused by assignment in a conditional (and gcc hates it).

For C++, I would prefer exceptions if available, otherwise the above.

Edit: I would recommend returning 0 on success and anything else on error. This is what unix command line utilities do.

Solution 2

If you really want to use status codes that way, use them with an enum or block of #define statements that describe the intention of the status code.

For example:

enum
{
   kSuccess = 0,
   kFailure = -1,
}

function foo()
{
    return kSuccess;
}

if (kSuccess == foo())
{
    // Handle successful call to foo
}
else
{
    // Handle failed call to foo
}

This way, the intention is clear and there's no error-prone guesswork when someone wants to use or maintain your code in the future.

Solution 3

if (foo()) {
  // what to do if false
} else {
  // what to do if true
}

The problem with this approach is excess nesting. Suppose you have three functions you want to call:

if(foo1()) {
    if(foo2()) {
        if(foo3()) {
            // the rest of your code
        } else {
            // handle error
        }
    } else {
        // handle error
    }
} else {
    // handle error
}

To solve the excess nesting problem, invert the return value:

if(!foo1()) {
    // handle error
    return;
}

if(!foo2()) {
    // handle error
    return;
}

if(!foo3()) {
    // handle error
    return;
}

This solution suffers from another problem. It mixes the program logic with the error handling code. This complicates everything. Ideally, you want the program logic and error handling separated. This problem can be fixed with the goto

if(!foo1()) 
    goto error1;

if(!foo2())
    goto error2;

if(!foo3())
    goto error3;

return;

error1:
    // handle error
    return;
error2:
    // handle error
    return;
error3:
    // handle error
    return;

Much cleaner.

Also, the goto can solve the problem of resource deallocation. See Using goto for error handling in C by Eli Bendersky for more details.

Solution 4

Best practice is to document your code so that yourself and others can quickly look up what the return codes will be when doing error checking.

Solution 5

The return statuses should be defined in your interface and known to the caller. Some return 0 on failure (because it's easy to check with !), some return 0 on success (because they have enum of error codes, with OK being the first item).

There's no law or standard, each interface defines its own conventions. In C++ - use exceptions.

Share:
16,076
Jason Marcell
Author by

Jason Marcell

I work at Apple in Safari and WebKit Operations, providing support for continuous integration, testing, deployment and other infrastructure and tooling needed to keep "The Internet" humming along quietly on your iPhone and Mac.

Updated on June 16, 2022

Comments

  • Jason Marcell
    Jason Marcell almost 2 years

    What are the best practices for writing C or C++ functions that return an int that represents a status code?

    Specifically, I want to know about the client usage but other tips are welcome.

    For example, can I write something like this:

    int foo() {
      return 0;  // because everything was cool
    }
    

    And then use it like this?

    if (foo()) {
      // what to do if false, e.g. non-zero, e.g. not OK
    } else {
      // what to do if true, e.g. zero, e.g. OK
    }
    

    This should work because best practices typically dictate that a status code of 0 means everything was OK and also 0 means false in a boolean statement.

    However, this wouldn't be good, right:

    if (!foo()) {
      // what to do if true
    } else {
      // what to do if false
    }
    
  • Andy Finkenstadt
    Andy Finkenstadt over 12 years
    // And armageddon sick of it, too. :-)
  • dario_ramos
    dario_ramos over 12 years
    Swallowing exceptions is bad practice, for lots of reasons: google.com.ar/…
  • Jason Marcell
    Jason Marcell over 12 years
    Wait, but "not"-ing something non-zero does not necessarily make it zero, right?
  • Jay
    Jay over 12 years
    "the result is 1 if the operand is 0, and the result is 0 if the operand is not 0." c.comsci.us/etymology/operator/logicalnot.html
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE over 12 years
    I would add that the reason it's usually best to return 0 on success and nonzero on error (as opposed to boolean 1/0 meaning true/false) is that there are usually many reasons an operation can fail but only one type of success.
  • Jason Marcell
    Jason Marcell over 12 years
    Yes, but since there is no first-class Boolean type in C, isn't the ! just an arithmetic negation on the integer operand? It's not a logical negation, right? It just flips the bits.
  • Jay
    Jay over 12 years
    so "not"-ins something non-zero does necessarily make it zero
  • Jay
    Jay over 12 years
    !10 is equal to 0, the article explains this
  • Jay
    Jay over 12 years
    btw, if you want actual boolean literals, there are a few options. See stackoverflow.com/questions/1921539/using-boolean-values-in-‌​c
  • adpalumbo
    adpalumbo over 12 years
    It's just a style thing, used to help constant values stand out from variables and other identifiers.
  • Jay
    Jay over 12 years
    You're confusing the not operator "!" with the bitwise not operator "~". They are two separate things. The "~" operator has the effect you're thinking of.
  • GManNickG
    GManNickG over 12 years
    @adpalumbo: But why do they need to stand out? :)
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE over 12 years
    Is it akin to Hungarian notation? Or is "k" an initial for your library/project/company? Or..?
  • adpalumbo
    adpalumbo over 12 years
    It appears in a lot of places: it's used in Hungarian notation, Apple's naming convention (which is not Hungarian), and many others. The real reason it appears in this sample is simply because it's used in my current employer's style guide, so its second nature for me now.
  • Oscar Korz
    Oscar Korz over 12 years
    In and of itself, that doesn't matter. The significance of 0 is that programmers are lazy and this allows the terse if(foo()) to work. Success could just as easily be 42, and we could all write if (foo() != 42)
  • GManNickG
    GManNickG over 12 years
    @Jason: C has a boolean type, in stdbool.h.
  • Alexander Oh
    Alexander Oh over 12 years
    @Jay: you should not do this with the resource deallocation. The crucial point is to have a single return for multiple error cases. imagine you have three resources allocated - one after every if you want to deallocate them in reverse order you would have three labels. then you would jump to the case where you deallocate all resources if you fail in the last if. and if you have to abort earlier you'd just deallocate 2 resources and jump to the penultimate label a.s.o. if you always want to deallocate all resources just skip the return in the middle.
  • Jason Marcell
    Jason Marcell over 12 years
    @GMan, thanks for pointing out stdbool.h. I had never heard of that before. However, I'll still point out that it just uses macros and also that this is part of a library and not the core language, so I might still argue that C does not have a boolean type. Granted, it is a "standard" library, so I guess you can count on it being there. So that's just about as good as it being part of the core language, right?
  • GManNickG
    GManNickG over 12 years
    @Jason: Sure. This statement is true: C has a boolean type. How you use that type is not a concern, but you are guaranteed that it's there in a well-defined manner. 'Core' or 'library', I think, is only a categorization within the language; note, then, that to even ask if it's part of the core or of the library, we already have to agree it's, at least, part of the language itself.