Why Return Codes Make Me Mad


In which I go on and on complaining about return codes, letting off some steam.

So, return codes. They make me mad. When given the choice, I always prefer exceptions.

Let’s say you have a function getValue(), which returns, well, the value of something. However, if something went wrong during setup, the value might never have been set, and we need to detect that. What we would like to write is

type value = getValue(); //For some type bool / int / WhateverClass

We do however need to detect error conditions. Either we use exceptions, or we use return codes. We can do either

try {
    type value = getValue();
} catch (NotInitializedException&) {
    //handle
}

or

type value;
bool ok = getValue(value)
if (!ok) {
    //handle
}

Either way you need to look out for errors, and maybe one isn’t so much worse than the other. What return codes do to the flow of you code however, is much worse. Here are some of the things that annoy me the most:

The size of the code explodes
An innocent, readable line like

if (getValue() == whatever)

turns into this mess:

type value;
getValue(value)
if(value == whatever)

Which one is easiest to read?

And of course, you can’t do this anymore:

finalResult = doStuffWith(getValue());

Variables have intermediate, invalid values
In the previous example, value needs to be constructed before getValue() is called. There’s a whole list of problems with this:

  • If it is a fundamental type (int, float, bool etc.) you have to manually initialize it, or you will have a dangerous uninitialized variable.
  • Some types don’t even have a default constructor
  • Some types are expensive to construct
  • Some constructors have side effects
  • value will have a meaningless value for a (short) part of the life of the program

You cannot use const variables.
I always make variables const whenever I can. This helps with program correctness, because I will never accidentally modify them, or pass them to a function that modifies them. But now you can’t do

const type value = getValue();

you have to do

type value; //Can't use const
getValue(value) //because it's modified here

Error checking code will infect your code and make it unreadable

Instead of handling errors in one place like this:

try {
    const type value = getValue()
    const othertype othervalue = getRelatedValue(value);
    doStuff(othervalue);
} catch (NotInitializedException&) {
    //handle;
}

You have to check for errors all the time:

type value;
bool ok = getValue(value);
if (ok) {
    othertype othervalue;
    bool otherok = getRelatedValue(othervalue, value);
    if (otherok) {
        doStuff(othervalue);
    } else {
        //handle
    }
} else {
    //handle
}

Look at this mess! Notice how the error handling code gets intermixed with the happy code. In the version of the code using exceptions, this is not necessary, as the rest of the try block is automatically skipped when an exception is thrown.

Constructors can’t even return a return code
The return value of a constructor always is the constructed object, so here you don’t even have a choice. If you don’t want to use exceptions, you need to remember that the object is now in an invalid state. So either you hope your users remember to do

SillyType s;
if (!s.gotProperlyConstructed() {
    //handle
}

or you need to make the newly constructed object remember that it is invalid, and return that as an error code indicating that every time someone calls a method on it from now on.

The code does not say what it really means
An argument to a function is an argument to a function. The return value of a function is the return value of a function. When reading a piece of code, this is the natural way to reason about it. When arguments are suddenly used for return values, the natural flow of the code is broken.

That’s not all
In addition, there’s a host of other problems like:

  • It’s easy to forget to check a return code, leaving your program silently in an invalid state. However, if you forget to catch an exception, you program blows up and at least you know something is wrong.
  • Operators can’t use return codes.
  • Exceptions tend to have useful information attached to them, describing the error. Return codes are usually enums, integers or booleans. (This can be fixed though, by returning an error object instead of an error code.)

Readable code
In all of the examples above, return codes lead to code that is less readable. And for me, readable code is extremely important. When your code is unreadable:

  • It is easier for bugs to hide
  • It is harder to make changes
  • It takes longer to get to know the code for new programmers (which might be yourself, half a year later)
  • It is usually harder to write tests
  • It is scary to refactor because you don’t understand the code fully, so you end up patching on another few lines and another few lines instead

In short, unreadable code leads to more bugs, longer development time for new features, more time spent in the debugger, and more time spent on maintenance. This is why I hate unreadable code. This is why return codes make me mad.

What can you do?
For new projects, go with exceptions. For legacy code you usually need to stay with the existing style. If all the code around you uses return codes, you probably should too. I would however argue that there still can be a place for exceptions: If you are extending the code base with an isolated module, or doing a major refactoring of one, and the interface to the rest of the code is small, use exceptions internally. Then catch the exceptions on the boundary and translate to return codes. After a while, you will gradually move towards exceptions and more readable code for the entire code base.

If you enjoyed this post, you can subscribe to my blog, or follow me on Twitter.

Why You Shouldn’t Throw in Destructors


In my post two weeks ago, What’s the Point of Function Try Blocks, I mentioned that you shouldn’t throw in destructors. Why exactly is this?

Imagine the following:

void f() {
  MyClass c;
  functionThatMightThrow();
}

If functionThatMightThrow throws, stack unwinding occurs. What this means is basically that all the objects on the stack get destructed, from the innermost block and out. In this case, c is destroyed before the exception is thrown out of f.

But what happens if ~MyClass() throws? There would now be two simultaneously active exceptions, something that luckily is forbidden by the standard. Instead, terminate() is called, and the whole program stops.

So what can you do? Design around it. Log and swallow. Ignore. Abort. If your class is managing some resource that should be cleaned up, but failing to do so is not critical, you could also provide a close() method that throws on error. A client who feels it is necessary to be 100% sure the resource is cleaned up will then have the option of calling close() and handling the exception himself, before your destructor is called.

Note that relying on the client to remember to call close(), setUp(), init() etc. is generally a bad idea, but might be acceptable in some cases. What makes it acceptable in this case is if cleaning up the resource is optional.

What’s the Point of Function Try Blocks


This post is a follow up to The Returning Function that Never Returned, which I wrote a couple of months ago. You can read it first if you want, it is only around a hundred words, but this post can be read on its own as well.

In my previous post, I presented a function with try outside of the braces. This is called a “function try block”. The example went like this:

std::string foo() try {
    bar();
    return "foo";
} catch (...) {
    log("Unable to foo!")
}

This is completely useless, even dangerous. The try should be moved inside the function itself, like this:

std::string foo() {
    try {
        bar();
        return "foo";
    } catch (...) {
        log("Unable to foo!")
        //either rethrow or throw something else
    }
    //or make sure something is returned
}

So why were function try blocks introduced to the language in the first place? Have a look at this:

class A : public B{
    A(int x) : c(x){}
    C c;
}

How do you catch an exception thrown by Cs constructor? Having try inside the function block is too late. Initializing c inside the function block is not a solution either, since c will always be initialized before the body of the constructor, even if it is not mentioned in the initializer list. The only way to catch such an exception is to use a function try block on the constructor, and that is also why they were introduced. (Note that this argument is also valid if Bs constructor might throw.)

This is also the only sane case in which to use them. The other to candidates are regular functions and destructors, both of which I will get back to in a moment.

First, let’s have a look at what you can do with a function try block on a constructor. When the catch block reaches its end, it will rethrow the exception. It is impossible to swallow it. If you could swallow the exception, the code that tried to construct an object of this type would have no way of knowing that construction failed. A failed construction means the object doesn’t exist, and it doesn’t make sense to continue pretending nothing has happened. The only thing you can do is to throw an exception of another type, or cause a side effect (such as logging) and then retrhow. In particular, you cannot try to recover from the problem.

The same goes for destructors, you cannot swallow the exception. And since you really should avoid having destructors throw, using function try blocks on destructors is a bad idea.

Try blocks on regular functions behave a bit differently; if the end of the catch block is reached, the function will automatically return. But if you have a non-void function, this doesn’t make sense at all, as I mentioned in my previous post.

So in conclusion:

  1. Only use function try blocks for constructors.
  2. Don’t try to do anything else than rethrowing (possibly another type) or cause side effects like logging.

For a more in-depth discussion of this, have a look at Sutter’s Mill: Constructor Failures (or, The Objects That Never Were) by Herb Sutter. If you would like a recap of exception handling and constructor initializers thrown in, I recommend to start with Introduction to Function Try Blocks by Alan Nash.

Puzzle #0: Call Sequence


Here’s a puzzle that should highlight a couple of interesting features in C++:

What is the output of the following program?

#include <iostream>;
using namespace std;

int main();

void term() {
    cout << "term()" << endl;
    main();
}

struct Positive {
    Positive(int i) {
        set_terminate(term);
        cout << "Positive::Positive(" << i << ")" << endl;
        if (i <= 0)
            throw main;
    }
};

Positive n(-1);

int main() {
    cout << "main()" << endl;
    Positive p(1);
}

If you just want the answer, you will find it at the bottom of this post. But first, I will go through the why.

Lines 1-2 are uninteresting. Lines 4-18 contain all the magic, but I’ll come back to those, I want to go through the program in chronological order as it is being executed. The entry point for this program is not main(), but rather the definition of Positive n(-1). This variable is global, and so will be initialized before main() is called. To initialize it, its constructor is called with i = -1, and so the real action starts on line 13.

On line 13, I use set_terminate() to set a termination handler. The termination handler will be called if a thrown exception is not handled. Otherwise, this statement has no visible immediate effect. We then encounter the first printout of this program, on line 14:

Positive::Positive(-1)

Our Positive class is however designed to only handle positive numbers, and throws on line 16. It is set to throw main, but this is really just a decoy. In C++, you can throw whatever you want, I happen to throw a function pointer to main(). In particular, this does not mean that main() is called.

The exception thrown on line 16 is never handled, and so the builtin terminate() calls the termination handler set_terminate(), which is defined on lines 6-9. Now we encounter our second printout:

term()

This is a straightforward cout on line 7. Then, we manually call main(). main is just a normal function, and even though it is special in that C++ will call it for you at startup, you are free to call it manually whenever you want. This is the third printout:

main()

main() then attempts to instantiate the local Positive object p(1). This calls the Positive constructor, which prints out

Positive::Positive(1)

This is a valid positive number, so no more exceptions are thrown. Control is returned to main(), which returns control to term(), which, being a termination handler, is not allowed to return, but still attempts to do so. I cannot find anything in the standard about what exactly is supposed to happen in this case, so I guess this is undefined behaviour. No matter what, returning doesn’t make any sense, as there is nowhere to return to. What happens on my system (gcc@linux) is program abortion with SIGABRT. What happens on yours?

Finally, the complete output of the program

$ ./outsidemain
Positive::Positive(-1)
term()
main()
Positive::Positive(1)
Aborted

This is probably not the most useful post I have written, but it highlights a few interesting points, and making a puzzle is always fun.

Always catch exceptions by reference


Some code I am responsible for at work recently got exposed to a new environment, and suddenly started logging exceptions. After some investigation, my colleague Thomas and I felt pretty sure what was going on, except the exception we saw in the logs was of a different type than the one that was thrown by the suspected culprit. What was going on, could the error be some place else after all? Then we looked at some intermediate code in the call hierarchy, and found the following (as usual, simplified) code :

class Exception{};
class SpecialException : public Exception {};

try {
    throw SpecialException();
} catch (Exception e) {
    cout << "Caught" << typeid(e).name() << endl;
}

Spot the problem? catch (Exception e) results in slicing. We are catching by value, which results in copying the exception. But since we are catching Exception, not SpecialException, Exception‘s copy construcor is called, and we lose the SpecialException part of the object. Here is an illustration of the Exception copy constructor in action:

Exception copy constructor slices away the SpecialException part of the object

On the left side is the full SpecialException object, with its Exception part shown in green. The green line illustrates the copy constructor, which only lets the Exception part of the object throuh.

The right way to write this would be catch (const Exception& e), which would result in a reference to the original SpecialException. Thanks to polymorphism, this program will now print “Caught SpecialException”, whereas the original example prints “Caught Exception”.

As a side note, always throw by value, not by pointer. That is, do throw Exception();, not throw new Exception();. After all, you want to be throwing an exception, not a pointer. And as always, there is no point in allocating on the heap if you don’t have to. Requiring people to clean up memory for you when catching is not very polite.

The returning function that never returned


Investigating a crash report yesterday, I came across this piece of code (simplified for the purpose of this post):

std::string foo() try {
    bar();
    return "foo";
} catch (...) {
    log("Unable to foo!")
}

int main() {
    std::string s = foo();
}

Today’s exercise: what happens if bar() throws? Obviously, the function doesn’t return, since the exception is thrown and control is passed to the catch block. But since this block does not return, and does not rethrow, foo() neither returns nor throws, so what goes into the string s? Answer: No one knows, we are left in the happy land called undefined behaviour.

The morale? If the original implementor had used -Wall, he would have gotten a warning that execution reaches the end of the non-void function. (Both Sun Studio and gcc happlily compiles without complaining otherwise.)

Update:I have posted a follow up going into more details on function try blocks.

If you enjoyed this post, you can subscribe to my blog, or follow me on Twitter