Return value for error

P

pozz

A very simple question. What do you use as the return value of a
function for error/ok results?
I can't decide between 0 for error and !=0 for ok, or 0 for ok and !=0
for errors.

If !=0 is ok, I can write:

if (myfunction()) printf("OK\n");

but I have only one error code (0), while, in some cases, it could be
useful to have more informations about the type of error.

If !=0 is error I can have several error codes, but I have to write

if (!myfuction()) printf("OK\n");

that apparently seems myfunction has not terminated with success.

Another strategy is to define custom error code:

#define ERROR_OK 0
#define ERROR_UNDEFINED 1
#define ERROR_WRONGVALUE 2

and so on, and always write:

if (myfuction() == ERROR_OK) printf("OK\n");
 
J

jacob navia

Le 03/05/11 10:31, pozz a écrit :
A very simple question. What do you use as the return value of a
function for error/ok results?
I can't decide between 0 for error and !=0 for ok, or 0 for ok and !=0
for errors.

If !=0 is ok, I can write:

if (myfunction()) printf("OK\n");

but I have only one error code (0), while, in some cases, it could be
useful to have more informations about the type of error.

Yes... That is the reason that option is not really useful.
If !=0 is error I can have several error codes, but I have to write

if (!myfuction()) printf("OK\n");

that apparently seems myfunction has not terminated with success.

I would recommend
if (myfunction() == SUCCESS)

where SUCCESS is zero
Another strategy is to define custom error code:

#define ERROR_OK 0
#define ERROR_UNDEFINED 1
#define ERROR_WRONGVALUE 2

and so on, and always write:

if (myfuction() == ERROR_OK) printf("OK\n");

The problem with "ERROR_OK" is that it is contradictory: An "OK error" ?

In the C containers library I decided (after much pondering) that:

Positive value: No error

Negative value: error code

Zero: Warning but OK.

For instance a function like POP(container) will return

< 0 if container == NULL. This is a hard error.
Zero: container was empty. Maybe this isn't an error.
0 one element was popped. This means everything is OK.

if (Pop(container) < 0) {
// error handling
}

Note that you can code other useful information in positive values.
You could return the number of elements still remaining in the stack
for instance.
 
I

Ike Naar

A very simple question. What do you use as the return value of a
function for error/ok results?
I can't decide between 0 for error and !=0 for ok, or 0 for ok and !=0
for errors.

If !=0 is ok, I can write:

if (myfunction()) printf("OK\n");

but I have only one error code (0), while, in some cases, it could be
useful to have more informations about the type of error.

If !=0 is error I can have several error codes, but I have to write

if (!myfuction()) printf("OK\n");

that apparently seems myfunction has not terminated with success.

Another strategy is to define custom error code:

#define ERROR_OK 0
#define ERROR_UNDEFINED 1
#define ERROR_WRONGVALUE 2

and so on, and always write:

if (myfuction() == ERROR_OK) printf("OK\n");

Another improvement would be to drop the ``ERROR'' prefix from
the constants (``ERROR_OK'' is a contradictio in terminis, isn't it?)
and replace it with a prefix that indicates that the constant has to do
with a particular function.
This helps if your program has several functions that follow the same
strategy.

#define FROBOZZ_OK 0
#define FROBOZZ_UNDEFINED 1
#define FROBOZZ_WRONGVALUE 2

#define WHACK_OK 0
#define WHACK_UNWHACKABLE 1
#define WHACK_ALREADYWHACKED 2

if (frobozz() == FROBOZZ_OK) printf("OK\n");
if (whack() == WHACK_OK) printf("OK\n");
 
I

Ian Collins

Another improvement would be to drop the ``ERROR'' prefix from
the constants (``ERROR_OK'' is a contradictio in terminis, isn't it?)
and replace it with a prefix that indicates that the constant has to do
with a particular function.
This helps if your program has several functions that follow the same
strategy.

#define FROBOZZ_OK 0
#define FROBOZZ_UNDEFINED 1
#define FROBOZZ_WRONGVALUE 2

I prefer an enum to #defines. It's more debugger friendly, less typing
and gives you a type to return.
 
J

James Kuyper

A very simple question. What do you use as the return value of a
function for error/ok results?
I can't decide between 0 for error and !=0 for ok, or 0 for ok and !=0
for errors.

If !=0 is ok, I can write:

if (myfunction()) printf("OK\n");

This is a perfectly reasonable approach, but when you use it, one of the
most important things is to make sure to name the function so that it
can be read as a question, and give it a non-zero value if the answer to
that question is "yes". Example from the C standard library: isalpha().
However, there's also lots of functions in the C standard library which
violate this rule; for instance, fseek().

If you can't think of a name for the function which would make this
work, then using this approach is probably not appropriate.

....
Another strategy is to define custom error code:

#define ERROR_OK 0
#define ERROR_UNDEFINED 1
#define ERROR_WRONGVALUE 2

and so on, and always write:

if (myfuction() == ERROR_OK) printf("OK\n");

If your code returns multiple different error codes, then somewhere you
should have a call to the function that actually does different things,
depending upon which error code is returned; a switch() is usually the
best way to do this. You can also have calls that don't make a
distinction; but if there aren't any calls to the function which do make
the distinction, then you don't really need to distinguish different
error conditions.

I'm guilty of violating that rule in my own code; but my intention is
that test drivers for the called routine will check which error code is
returned, even if the actual delivered program does not.
 
J

James Dow Allen

As James Kuyper says, best is to have the return value
implicit in the function name:
is_kosher(this.soup) && do_serve(&this);
or
dad = find_father();
if (dad == NULL) {
/* process orphan */
}

Letting zero represent incorrect/error is often good enough:
many simple programs will handle all error returns from a
simple function the same, so a single error "reason" is enough.

James
 
S

Stefan Ram

pozz said:
I can't decide between 0 for error and !=0 for ok, or 0 for ok and !=0
for errors.

0 means »no error«, since there is only one way to
succeed, but many ways to fail.
if (!myfuction()) printf("OK\n");

Yes, the »!« looks ugly there, but often one wants to handle
the error in some way:

if( int const code = alpha() )error( code ); else printf ...

(I know that one cannot define variables in the
if expression, but it would be nice.)
 
M

Mark Storkamp

0 means »no error«, since there is only one way to
succeed, but many ways to fail.


Yes, the »!« looks ugly there, but often one wants to handle
the error in some way:

To steal a page from BCPL you can always do something like this:

#define unless(c) if(!(c))

Then you have

unless (myfunction()) printf("OK\n");
 
S

Stephen Sprunk

A very simple question. What do you use as the return value of a
function for error/ok results?
I can't decide between 0 for error and !=0 for ok, or 0 for ok and !=0
for errors.

General rules:

1. If one success state and many error states, let 0 indicate success.

2. If one error state and many success states, let 0 indicate error.

3. If many success states and many error states, let negative indicate
error and non-negative indicate success.
If !=0 is ok, I can write:

if (myfunction()) printf("OK\n");

but I have only one error code (0), while, in some cases, it could be
useful to have more informations about the type of error.

This is usually the case.
If !=0 is error I can have several error codes, but I have to write

if (!myfuction()) printf("OK\n");

that apparently seems myfunction has not terminated with success.

You could consider rewriting that line as:

if (myfuction()==0) printf("OK\n");

That form has the same meaning to the compiler, but some folks feel it
conveys a different/better meaning to humans. This frequently comes up
in debates about strcmp() et al, for example.

S
 
K

Keith Thompson

James Kuyper said:
This is a perfectly reasonable approach, but when you use it, one of the
most important things is to make sure to name the function so that it
can be read as a question, and give it a non-zero value if the answer to
that question is "yes". Example from the C standard library: isalpha().

Right. The important distinction here is that the result of isalpha()
is purely a yes/no result, *not* a success/failure result. If isalpha()
could tell you either
Yes, that's an alphabetic character
or
No, that's not an alphabetic character
or
Oops, I can't tell what that is
the it would need a different interface. In fact, none of the is*()
functions have any way to indicate failure.
However, there's also lots of functions in the C standard library which
violate this rule; for instance, fseek().

I wouldn't say fseek() violates the rule, it just follows a different
one. It attempts to perform an action (seeking to the specified
position) and returns a result indicating either that it succeeded, or
that it failed. The common convention is to use 0 for success, non-0
for failure. (It doesn't use different non-0 values to denote different
kinds of failure.)
If you can't think of a name for the function which would make this
work, then using this approach is probably not appropriate.

Agreed. But even if the function answers a yes/no question, you still
have to think about cases where the question can't be answered. Most
yes/no functions in the standard library don't have ways to indicate
errors (see is*(), feof(), ferror()).

[...]
 
K

Keith Thompson

pete said:
When there may be several various error return codes,
then use 0 for success.

When there are *not* several various error return codes,
such as in the isspace function
and the the other <ctype.h> functions,
then use 0 for failure.

As I said in another followup, the is*() functions don't return
0 for *failure*. A return value of 0 means that the function
successfully answered the question -- and the answer was no.
 
K

Keith Thompson

pozz said:
A very simple question. What do you use as the return value of a
function for error/ok results?
I can't decide between 0 for error and !=0 for ok, or 0 for ok and !=0
for errors.

If !=0 is ok, I can write:

if (myfunction()) printf("OK\n");

but I have only one error code (0), while, in some cases, it could be
useful to have more informations about the type of error.

If !=0 is error I can have several error codes, but I have to write

if (!myfuction()) printf("OK\n");

that apparently seems myfunction has not terminated with success.

Just because you're checking whether a result is equal to zero, you
don't *have* to use the "!" operator.

Personally, I use "!" only for values that are logically Boolean
(zero means false, anything non-zero means true, and there's no
meaningful distinction among different non-zero values). So I
might write the above as:

if (myfunction() == 0) printf("OK\n");

Or I might define a constant of some sort with the value 0
to make the code clearer, as you suggest:
Another strategy is to define custom error code:

#define ERROR_OK 0
#define ERROR_UNDEFINED 1
#define ERROR_WRONGVALUE 2

and so on, and always write:

if (myfuction() == ERROR_OK) printf("OK\n");

(but not with a name that's reserved to the implementation).
 
J

Jorgen Grahn

As James Kuyper says, best is to have the return value
implicit in the function name:
is_kosher(this.soup) && do_serve(&this);
or
dad = find_father();
if (dad == NULL) {
/* process orphan */
}

Letting zero represent incorrect/error is often good enough:
many simple programs will handle all error returns from a
simple function the same, so a single error "reason" is enough.

Yes. In my experience, lots of people want to be as generic as possible
and let pretty much *everything* have 0 for success and non-zero for
failure. This makes the calling code much harder to read.

It's worth /a lot/ to have calling code which reads naturally, like J.D.A.'s
examples above. Not like strcmp() or many POSIX functions.

/Jorgen
 
P

Peter Nilsson

Kenneth Brody said:
Nit:

Those are, technically, all reserved for implementation-
defined error codes.
  (7.26.2 reserves "macros that begin with E and a digit
or E and an uppercase letter".)

Strictly only in the precence of <errno.h>, but it's still best
to avoid EXXX macro names. Header include guards are a common trap.
 
K

Kleuskes & Moos

Right.  The important distinction here is that the result of isalpha()
is purely a yes/no result, *not* a success/failure result.  If isalpha()
could tell you either
    Yes, that's an alphabetic character
or
    No, that's not an alphabetic character
or
    Oops, I can't tell what that is
the it would need a different interface.  In fact, none of the is*()
functions have any way to indicate failure.

Perhaps that's got to do with the fact that they cannot actually fail.
Whatever the input, it's always possible to say wether it codes for an
letter, a number, upper or lower case character or not.

However, for functions that might actually fail, the consideration is
actually an important one.
 
K

Keith Thompson

Kleuskes & Moos said:
Perhaps that's got to do with the fact that they cannot actually fail.
Whatever the input, it's always possible to say wether it codes for an
letter, a number, upper or lower case character or not.

Well, sort of. The argument of the is*() function is an int,
but its value is required to be representable as an unsigned char
or to be equal to EOF. The standard *could* have specified some
kind of error handling for isalpha(UCHAR_MAX + 1), but instead it
just says the behavior is undefined. Similarly, the behavior of
feof(FILE*(NULL)) is undefined.

The choice was between imposing a small burden on the caller to
avoid passing bad arguments, and making the interface substantially
more inconvenient.
However, for functions that might actually fail, the consideration is
actually an important one.

Right. If you want to have a function that returns a yes/no result,
but the caller can't reliably avoid passing invalid arguments,
you might have it return an error code (zero for success, non-zero
for error) and pass the actual result via an int* argument.

(Languages with exception handling generally don't have this problem.)
 
K

Kleuskes & Moos

Well, sort of.  The argument of the is*() function is an int,
but its value is required to be representable as an unsigned char
or to be equal to EOF.  The standard *could* have specified some
kind of error handling for isalpha(UCHAR_MAX + 1), but instead it
just says the behavior is undefined.  Similarly, the behavior of
feof(FILE*(NULL)) is undefined.

Aye, it could have introduced a check, and it might have ordained
array-bounds-checking, too, but the fact of the matter is it doesn't.
and it seems aginst the spirit of C, too. Besides, returning false for
isalpha(65535) is a very reasonable implementation of that particular
bit of undefined behavior and I suspect most compilers will do.
The choice was between imposing a small burden on the caller to
avoid passing bad arguments, and making the interface substantially
more inconvenient.

Yup. And the ghost of C demands it's the programmers responsibility.
Fortunately C is not Pascal.
Right.  If you want to have a function that returns a yes/no result,
but the caller can't reliably avoid passing invalid arguments,
you might have it return an error code (zero for success, non-zero
for error) and pass the actual result via an int* argument.
Ack.

(Languages with exception handling generally don't have this problem.)

:)

Instead they have other problems. The Law of Conservation of Misery is
unrelenting...
 
K

Keith Thompson

Kleuskes & Moos said:
Aye, it could have introduced a check, and it might have ordained
array-bounds-checking, too, but the fact of the matter is it doesn't.
and it seems aginst the spirit of C, too. Besides, returning false for
isalpha(65535) is a very reasonable implementation of that particular
bit of undefined behavior and I suspect most compilers will do.

In my experience, at least some implementations define macros for
the is*() functions, macros that work by indexing into arrays.
isalpha(65535) will go well outside the bounds of the array,
with unpredictable results. (At least one implementation I've
seen defensively allows for small negative arguments, so (signed)
char arguments work as they should, even though the standard doesn't
require them to. But you shouldn't depend on that.)

[...]
 
N

Nobody

(7.26.2 reserves "macros that begin with E and a digit or E and an
uppercase letter".)

Yeah, but that's just the standard being greedy. No common implementation
has error macros which include an underscore. Most vendors are aware that
what the standard allows is less important than what their customers will
allow.
 
B

Ben Bacarisse

Nobody said:
Yeah, but that's just the standard being greedy. No common implementation
has error macros which include an underscore.

I'll take your word for that, but every conforming implementation does
have macros that start with a E and which do include an underscore
(EXIT_SUCCESS and EXIT_FAILURE).

<snip>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top