Why to use 'errno' at all?
The main reason for using errno
is to give more information about the error condition.
This is especially useful in situations where most (or even all) possible return values of a function are actually valid return values.
Consider the fopen()
function, which returns a pointer to a FILE
. Every possible return value is also a valid return value, except NULL
. So fopen()
returns NULL
on failure. But then you can't tell what exactly made the function fail. Hence, fopen()
uses errno
to denote the exact error condition, i.e. the file doesn't exist, or you don't have permission to read it, or the system is out of memory, or whatever.
You can think of errno
as a global variable (which it used to be until threads became popular). Nowadays, errno
is usually a macro wrapping a function call returning the error condition. But this is just C's way of implementing thread-specific global variables.
The alternatives to errno
are less comfortable:
You could supply a function with a pointer to an int
, and the function can store its error condition there. strtod()
is a good example of this technique. But this makes the API more complicated and hence less desirable. Also, it forces the programmer to define a new int
, which is annoying if you don't care if the function fails.
In languages that allow more than one return value (and don't feature exceptions), it's common to return two values: one for the actual result and another to denote the error condition. In languages like Go, you see code like the following:
result, ok = foo();
if (ok) {
// handle error denoted by "ok"
}
Don't trust people who claim that errno
is an "old" technique and hence to be avoided. The machine you're programming is far older than errno
or even C, and nobody has ever complained about that.
The design of the C library was done a long time ago at the same time as early Unix. Use of a separate error code is not an uncommon pattern (Win32 has the similar GetLastError()). And it has superficial advantages.
If you decide you want a class of functions to have a return value that is commonly used, then you can't easily use that to return errors. For example, imagine a hypothetical API
mytime_t t = get_current_time();
The common use of this API is to get time. But perhaps it can fail in some circumstances, and you get detailed error information from errno. This makes the API code a bit easier to read and write than if you had to say
mytime_t t=0;
errno_t e = get_current_time(&t);
So superficially, errno-type systems are appealing. However, the separation of error state from the actual function call leads to lots of problems. Modern environments make errno_t a per-thread variable (removing the most obvious source of problems), but you still face the problem that if you do
mytime_t t = get_current_time();
mysize_t s = get_window_size();
then you destroy the errno from the first function invisibly. This gets even more complex when code can run in error paths or where one function is implemented in terms of others. Lots of saving and restoring errno values ensures. I think it's pretty well accepted now that such systems are fragile and undesirable.
Many folks use exceptions which carry errors outside the explicit parameter/return set (many C++ programmers make this choice). Folks working in exception-free languages or environments tend to bite the bullet and reserve the return value for an error and always provide output through parameters (COM makes this choice with HRESULTs).
errno
is a complicated thing, in that it is an historic interface that probably nowadays nobody would design like that. In addition on most systems it nowadays only looks like a variable, it ain't one. Usually it is implemented as a macro that hides a function call, and that function call returns a thread specific error condition.