experchange > programmer

Steve Keller (07-04-19, 02:51 PM)
I have a function foo() that can fail for several reasons, i.e.
because some function in libc fails and sets errno, or because some
other error occurs in foo() itself. I don't want foo() to print to
stderr but only to return an error code to the caller.

The problem is that sometimes the error code is in errno and sometimes
it's some error code of my own for which no suitable error name is
defined in errno.h.

How can I elegantly handle this situation?

Currently, I have something like this:

#define ERR_SYS (-1)
#define ERR_FOO (-2)
#define ERR_BAR (-3)
...

int foo() {
if (open(...) < 0)
return ERR_SYS;
else if (some_condition_for_foo_failure)
return ERR_FOO;
else if (some_condition_for_bar_failure)
return ERR_BAR;
...
return 0;
}

void print_error(int errcode) {
switch (errcode) {
case ERR_SYS:
perror(...);
break;
case ERR_FOO:
fprtinf(stderr, "Foo failure\n");
break;
...
}
}

main() {
...
if ((errcode = foo()) < 0)
print_error(errcode);
}

Is there a more beautiful way to do it?

Steve
Kaz Kylheku (07-04-19, 07:45 PM)
On 2019-07-04, Steve Keller <keller> wrote:
> I have a function foo() that can fail for several reasons, i.e.
> because some function in libc fails and sets errno, or because some
> other error occurs in foo() itself. I don't want foo() to print to
> stderr but only to return an error code to the caller.
> The problem is that sometimes the error code is in errno and sometimes
> it's some error code of my own for which no suitable error name is
> defined in errno.h.
> How can I elegantly handle this situation?


Oh, let me count the ways.

1. Separation of ranges: starting at STEVE_ERROR_BASE are your errors;
values below that are errno.

2. Encoding into an aggregate small structure, or word:
- lowest 16 bits (or whatever) are your error code.
- some range of bits above that are errno; if that is zero, there
error word represents a situation that doesn't map to an errno.
- macros/inlines are provided for constructing and destructuring the
compound code.

> Currently, I have something like this:
> #define ERR_SYS (-1)
> #define ERR_FOO (-2)
> #define ERR_BAR (-3)
> ...
> int foo() {
> if (open(...) < 0)
> return ERR_SYS;


return MAKE_STEVE_ERROR(errno, ERR_SYS);

> else if (some_condition_for_foo_failure)
> return ERR_FOO;


return MAKE_STEVE_ERROR(0, ERR_FOO);

> else if (some_condition_for_bar_failure)
> return ERR_BAR;
> ...
> return 0;
> }
> void print_error(int errcode) {
> switch (errcode) {


switch (GET_STEVE_CODE(errcode)) { /* just the code field */

> case ERR_SYS:
> perror(...);


{
int eno = GET_STEVE_ERRNO(errcode);
printf("ERR_SYS result contains this errno: %d/%s\n", errno,
strerror(errno));
}

Probably we shouldn't use perror for this kind of thing where there are
layers of API separation between the error and the reporting. perror
accesses the current errno value, which could change by the time we get
here, no longer reflecting the original situation.
Rainer Weikusat (07-05-19, 08:39 PM)
Steve Keller <keller> writes:
[..]
> ...
> return 0;
> }


That's the common way to do this. Another idea would be to return -errno
in case of a system error, 0 on success and some positive error code for
"another kind of error".
James K. Lowden (07-08-19, 10:16 PM)
On Thu, 4 Jul 2019 17:45:16 +0000 (UTC)
Kaz Kylheku <847-115-0292> wrote:

> 2. Encoding into an aggregate small structure, or word:
> - lowest 16 bits (or whatever) are your error code.
> - some range of bits above that are errno; if that is zero, there
> error word represents a situation that doesn't map to an errno.
> - macros/inlines are provided for constructing and destructuring
> the compound code.


This.

Sometimes your function will encounter an error that sets errno, and
returning that information (perhaps augmented by what it was doing when
the error was encountered) is the best you can do. Other times the
caller will fail to meet preconditions and your function can't start,
or starts and can't finish when it discovers the precondition wasn't
satisfied. That's another kettle of fish.

If you use enumerated errors for precondition failures and let errno be
errno, the caller has all available information. One of your errors
can be simply "OS error", where the "real" error is found in errno.

--jkl
Similar Threads