Getting back in to some C work.
Many of my functions look like this:
int err = do_something(arg1, arg2, arg3, &result);
With the intent the result gets populated by the function, and the return value is the status of the call.
The darkside is you get something naive like this:
int err = func1(...);
if (!err) {
err = func2(...);
if (!err) {
err = func3(...);
}
}
return err;
I could macro it I suppose:
#define ERR(x) if (!err) { err = (x) }
int err = 0;
ERR(func1(...));
ERR(func2(...));
ERR(func3(...));
return err;
But that only works if I'm chaining function calls, vs doing other work.
Obviously Java, C#, C++ have exceptions that work very well for these kinds of things.
I'm just curious what other folks do and how other folks do error handling in their C programs nowadays.
What are you doing in the
else
statements? If nothing, try this:This way you're short-circuiting the entire function, not even bothering with the following function calls.
EDIT
Going back and reading again, I realize that it doesn't matter what you do in your
else
statements. That sort of code can easily go immediately after theif
blocks.Others have suggested good ideas. Here're the idioms I've seen
You could macro this out to something like
or, if you're feeling really motivated, fiddle with CALL and CHECK so they can be used like
or
--
Often, functions which need to do cleanup on exit (e.g. free memory) are written like this:
You could use that pattern, or try to simplify it with macros.
--
Finally, if you're feeling /really/ motivated, you can use setjmp/longjmp.
Essentially, a declaration of a new jmp_buf and a setjmp function as setting up a try block. The case where setjmp returns non-zero is your catch, and calling longjmp is your throw. I wrote this with passing the jmp_buf around in case you want nested handlers (e.g. if you need to free stuff before signaling an error); if you don't need that, feel free to declare err and the jmp_buf as globals.
Alternately, you could use macros to simply the argument passing around. I'd suggest the way Perl's implementation does it:
--
Phew. I'm done. (Crazy examples untested. Some details might be off.)
If you have resources that need to be released at the end, then sometimes the old trusty
goto
can be handy!Something I've recently seen is this idom:
Pro arguments:
It avoids the goto (abuses the while(0) / break combination for that). Why would you want to do this? It keeps the cyclomatic complexity down and will still pass most static code analyzer checks (MISRA anyone?). For projects that get tested against cyclomatic complexity this is a god sent because it keeps all the initialization stuff together.
Contra arguments:
The meaning of the do/while loop construct is not obvious because a loop-construct is used as a cheap goto replacement, and this can only be seen at the loop tail. I'm sure for the first time this construct will cause lots of "WTF"-moments.
At least a comment is necessary to explain why the code is written the way it is required.
And now for something completely different...
Another approach is to use a struct to contain your error information, e.g:
The best way to use this is to return your method's results as the return code (e.g. "FALSE for failed", or "a file pointer or NULL if it fails", or "size of the buffer or 0 if it fails", etc) and pass in an ErrorInfo as a parameter that the called function will fill in if something fails.
This gives rich error reporting: if the method fails, you can fill in more than a simple error code (e.g. error message, code line and file of the failure, or whatever). The nice thing about it being a struct is that if you think of something, anything, useful later, you can just add it - for example, in my struct above I've allowed for a debug build to include the location of the error (file/line), but you could add a dump of the whole call stack in there at any time without having to change any of the client code.
You can use a global function to fill in an ErrorInfo so the error return can be managed cleanly, and you can update the struct to provide more info easily:
...and you can have variants of this function that return FALSE, 0, or NULL, to allow most error returns to be phrased as a single line:
This gives you a lot of the advantages of an Exception class in other languages (although the caller still needs to handle the errors - callers have to check for error codes and may have to return early, but they can do nothing or next-to-nothing and allow the error to propagate back up a chain of calling methods until one of them wishes to handle it, much like an exception.
In addition, you can go further, to create a chain of error reports (like "InnerException"s):
Then, if you "catch" an error from a function that you call, you can create a new, higher-level error description, and return a chain of these errors. e.g. "Mouse speed will revert to the default value" (because) "Preference block 'MousePrefs' could not be located" (because) "XML reader failed" (because) "File not found".
i.e.
You should check out what DirectX has done with the HRESULT - it's basically this. There's a reason that the exception came into being. Alternatively, if you run on Win32, they have SEH which runs in C programs.