Definitely check anything that has any reasonable likelihood it could fail, such as file access, or interfacing with the graphics API, or audio devices. I usually try to “bubble up” any errors to the wrapping function, so that they can be handled by code with more context about how to recover from the error. In my mutex wrapper I return a bool from lock()/unlock() functions, though I don't believe I ever check that return value. On windows, the critical section functions don't even have return values, so I assume they always succeed.
There are a few ways to handle errors:
- assert() - this is good for high-performance code or for things that are only likely to fail due to programmer error, such as passing in invalid arguments to a function. These asserts can be omitted in release/optimized builds so that there is no performance hit in released code. A failing assert crashes the program and tells you where it failed, so that you can correct the programming error. Don't use asserts for things that can fail for other reasons. I have asserts turned on during development (even in optimized builds) to catch errors.
- Return codes/bools - I use these for things that are more likely to fail due to circumstances outside the control of the programmer, such as if a file doesn't exist. With this method, you need to be careful to propagate errors up the call stack manually. Generally I find bool to be better than integer return codes for this. If you need to indicate a specific type of error occurred, print to a centralized logging system.
- Exceptions - not recommended, due to potential for unexpected behavior. Exceptions pollute your code with try/catch nonsense, and bloat your executable. In code that uses exceptions, there is the possibility that any function you call could throw, which means to be safe you would need try/catch almost everywhere to clean up stuff to avoid resource leaks. To some extent you can avoid this with the RAII paradigm, but it's no panacea. Most game developers and other writers of high-performance code mandate that exceptions be disabled at the compiler level, to avoid their problems and overhead. I saved like 15% on binary size by turning off exceptions, even though I don't use them at all.
In my codebase I heavily check errors for most 3rd party libraries and APIs, and turn those into a simple bool+log if necessary, for ease of error propagation. Usually errors only propagate up no more than a few function calls before it can be recovered from, if your code is designed well.
Another pattern that can be useful in the “return code” paradigm is to create a “result” class, which is a simple wrapper around an integer return code or enum. This class has an operator for conversion to bool, which makes it easy to see if the function succeeded, yet allows the specific error to also be known:
class Result
{
public:
Result( int newCode ) : code( newCode ) {}
operator bool () const
{
return code != ERROR;
}
int code;
};
I use this in my audio engine, where the process() function on an audio effect can return a few different values depending on what happened: SUCCESS, ERROR, SILENCE. SILENCE is not an error, and indicates that the audio output of the effect should be assumed to be all 0.
Looking at that android code, I'd say it doesn't have enough error checking, in fact there is almost none.