Returning Usable Values in C

I program in C a lot. Like, 90% of the code I write is C code. The main reason for this is that I’m stuck in my ways; C was the first language I learned after getting hooked on programming with Visual Basic 6. Additionally, it is for several reasons (which I might outline one day) my favourite programming language, and is certainly the one I know best.

One of the major considerations when writing any non-trivial program in C is how you plan on returning error codes from functions. How does code calling the functions you write know whether or not your function succeeded?

There are multiple traditionally-used approaches to this problem, but I try to follow two simple rules when writing functions:

  1. If the function is a predicate, name it as such (e.g. player_exists()) and have it return boolean true or false.
  2. If the function is a command, for example do_work(), and it doesn’t return a usable value to its callers, then have it return zero on success and minus one on failure, as is typical in C. If it does need to return a usable value which is a pointer, then I return NULL on error. If it needs to return a non-pointer value, then I return a retval_t.

What the heck is a retval_t? It’s similar to the optional<T> from the C++ Boost library, and Haskell’s Maybe. It’s a simple two-element structure containing a void pointer and a boolean value, like so:

struct retval_t {
void *data;
int valid;

(Note: I don’t actually write code without indentations. I just don’t know enough HTML / CSS to get tabs in there and don’t care enough to learn how to do it properly right now.)

I had considered making the data a tagged union, and I could have some macros to make declaring the things a little less of a chore, but I decided against it. (I always try to err on the side of minimalism until it’s proven that something more complicated is required.)

It seems like too much messing about at first glance, and a fair number of you are probably wondering why I don’t just use C++ and Boost. In practice it’s not so bad, because you don’t need to return usable non-pointer values that often. As for C++… No. I will probably end up writing a rant about this in the future, but… just no.

I really do enjoy writing things “properly” in C. Well I do in any language, but especially in C. Proper error checking and handling, judicious memory management, the lot. Probably because I just enjoy firing up valgrind on a basic, nerdy level. The same way I just enjoy programming on a basic, nerdy level. It stirs up something primal within me, even if I’m using a language and styles considered “antiquated” or “no longer relevant”. I have occasionally been guilty of using C where a different, higher-level language would have sufficed, but I don’t care. It’s fun.

Anyway, with a retval_t we can write code like this:

// ...
retval_t r = some_function();
if (r.valid) {
// use ...
} else {
// panic, abort, log error, etc.

Which I quite like. It saves having to remember or look up what some_function() returns when something has gone wrong, and if it’s returning a usable non-pointer value then it really could be anything. Some out of bounds value, usually, but in C there is no universal out of bounds value, it depends on the type involved. (NULL is the only obvious exception, hence my rule regarding it.)

So like it or dislike it, this is one of the ways I (ab)use the C language. More to follow.

(Closing comment: I actually dislike the name retval_t. Plenty of people think the something-underscore-t naming convention is disgusting and should be taken out the back and shot, but I’m not talking about that. I just think “retval” is a stupid name for it. I mean yes, it is a return value, but still. I originally called it maybe_t but that annoyed me because I felt I would have had to use a tagged union in that case, so it didn’t make sense. “Optional” makes even less sense to me. Any suggestions?)


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s