exit()

B

BartC

Bart van Ingen Schenau said:
Would it still be counter-intuitive if you regard the result of an
application not as a boolean, but as a status code (with 0 == "success",
EXIT_FAILURE == "generic failure", other values more specific failure
codes)?
Or do you then also use 1 for success and several values of 0 to indicate
the various failures?

For status returns, the patterns I use tend to look like this:

o 1 = Success, 0 = Failure

Used when no further error codes are needed (or when there is a separate
step to obtain them).

This matches the logic typically used in programming:

if (do_it()) means it's done it. It would be odd to use it like this:

if (do_it()) puts("Couldn't do it!");

Even worse if the function was named done_it().

o N = Success, 0 = Failure

Where N encodes some data such as a file handle. This is used a lot in C
such as in malloc() and fopen().

I also use it for searches (in 1-based systems), where N is the index of
where something has been found, and 0 means not found.

o N>0 = Success, M<=0 means Failure (including N=1)

Where extra error info is needed; or more typically, where an 'out-of-band'
status is needed,
(for example on a dialog box where either item N is selected, or action M
has been clicked).

I don't use the 0 = Success, Anything Else = Failure pattern much at all,
except when an external API function happens to use it, or when 0 is part of
the data-range being returned.
 
S

Shao Miller

Can I use the return value provided to exit() for my own purposes?

(This is so that I can pick up the exit-value when I invoke the program
from
another. BTW what's the best way of invoking a program under Linux so that
this value can be retrieved?)

In Windows driver development, there is a common pattern:

void Foo(void) {
NTSTATUS status;

status = Bar();
if (!NT_SUCCESS(status)) {
/* Problem */
}

/* On to the next thing */
status = Baz();
/* ... */
}

You could consider writing your own 'NT_SUCCESS' counterpart macro, or a
wrapper for 'system', which could do different things for different
implementations. (#ifdef can be handy.) By using an identifier such as
'NT_SUCCESS', you get some descriptive code instead of requiring a
reader to understand the subtleties of the meaning of specific values in
a Boolean context. It so happens that 'NT_SUCCESS' actually accepts
some non-zero values, too, and so could you.
 
K

Keith Thompson

BartC said:
For status returns, the patterns I use tend to look like this:

o 1 = Success, 0 = Failure

Used when no further error codes are needed (or when there is a separate
step to obtain them).

That's fine -- but then it would make more sense to use bool rather than
int (unless you're stuck with a pre-C99 compiler).

[...]
I don't use the 0 = Success, Anything Else = Failure pattern much at all,
except when an external API function happens to use it, or when 0 is part of
the data-range being returned.

But we're specifically talking about an external API that *does* use 0
for success, anything else for failure.

What's so difficult about comparing (result == 0) to determine whether
something succeeded?
 
B

BartC

But we're specifically talking about an external API that *does* use 0
for success, anything else for failure.

What's so difficult about comparing (result == 0) to determine whether
something succeeded?

When dealing directly with such a function, then obviously I will need to do
that. But it needs to fit it with the way I normally do things, so I will
use a wrapper in that case.

Taking the example of an 'int compile_gcc(char* file) function, that might
include the lines:

if (system(cmdstr)==0)
return 1;
else {
<arrange the display of the error messages>
return 0;
}

Here, an error situation isn't well represented by a range of integer codes.
It's quite sufficient to return 0=Failure; the caller knows it can't then
proceed with any follow-up dependent operations, while the actual reasons
are given by the contents of a file capturing stderr. And in any case gcc
only seems to ever return 0 or 1.
 
K

Keith Thompson

BartC said:
When dealing directly with such a function, then obviously I will need to do
that. But it needs to fit it with the way I normally do things, so I will
use a wrapper in that case.

Ok. I still think you're refusing to accept that a widely used
program execution model could possibly be valid, because it conflicts
with the way you would have preferred to do it, but it's your call.

There are a number of C standard library functions that return
0 for success, non-0 for failure. A few examples: atexit(),
fraiseexcept(), fegetenv(), raise(), remove(), rename(), setvbuf(),
fseek(), fsetpos(); searching section 7 of the standard for "nonzero"
will turn up more such cases.

I suggest you'll spend more time putting wrappers around things that
return status values rather than booleans than you'd spend just using
these functions as they're designed.
Taking the example of an 'int compile_gcc(char* file) function, that might
include the lines:

if (system(cmdstr)==0)
return 1;
else {
<arrange the display of the error messages>
return 0;
}

Then I suggest giving your function a name that suggests that it returns
a boolean (in the sense of logically true/false, not necessarily _Bool)
result. The name "compile_gcc" would be reasonable for a void function
that invokes the compiler (or that compiles the compiler!). It doesn't
suggest that it's a predicate, returning true or false depending on
whether the compilation succeeded or failed.

I'd also suggest having it return a bool result rather than an int, to
emphasize that the result can have one of only two values.

Perhaps something like

bool gcc_ok(char *file);
Here, an error situation isn't well represented by a range of integer codes.
It's quite sufficient to return 0=Failure; the caller knows it can't then
proceed with any follow-up dependent operations, while the actual reasons
are given by the contents of a file capturing stderr. And in any case gcc
only seems to ever return 0 or 1.

I can't find anything about return status in the gcc documentation,
which is disappointing. Looking at the source code, it can exit
with a status other than 0 or 1 in some circumstances. It should
be safe to assume that 0 denotes success, and anything non-0 denotes
failure of one sort or another (which could be anything from a syntax
error in the program being compiled to a corrupted executable for
the preprocessor).
 
J

Jorgen Grahn

Sometimes I need to return two values (usually 1 for success, 0 for failure;
the opposite of above); sometimes three or four (an exit code from an editor
for example that may contain a request to compile the result).

How can the editor tell? Depending on exactly what you mean, there may
be better patterns for dealing with this:

- Mailers which call on an editor where you're supposed to write your
mail set up a template for you, then start the editor. If it exits
successfully and you've changed the file, it tries to send it as a
mail.
- Similarly for 'git commit' or 'cvs ci'. In some cases the editor
needs to exit successfully and the file must be non-empty, or
the caller aborts the operation.

/Jorgen
 
N

Nathan Wagner

My advice: get used to the fact that Unix commands (and, I think,
many Windows commands as well) return a status of 0 for success,
non-0 for failure.

The reason is that there's typically only one way for a program to
succeed, and many ways for it to fail.

"Happy families are all alike; every unhappy family is unhappy in its
own way."

And with that Tolstoy anticipates the unix architecture.
 
B

Ben Bacarisse

Nathan Wagner said:
"Happy families are all alike; every unhappy family is unhappy in its
own way."

And with that Tolstoy anticipates the unix architecture.

Message-ID: <0.50886b21700c15f4e1e3.20130217171641GMT.87r4kevp1i.fsf@bsb.me.uk>
Great minds think alike?
 
N

Nathan Wagner

Message-ID:
<0.50886b21700c15f4e1e3.20130217171641GMT.87r4kevp1i.fsf@bsb.me.uk>
Great minds think alike?

Apparently. That's what I get for not reading c.l.c for a week and then
posting without catching up on the whole thread. A day late and a
dollar short. Or six days (and six dollars?) if the reading schedule
is particularly languid. Bah.

ObTopic: So, how do we characterize a usenet posting in a return code?
If we use exit(0) for failure, and exit(1) for success, we don't have
available a simple boolean test for "can I assume it worked, or do
I need to investigate further", yet here, we could define other
exit codes than EXIT_FAILURE (though it wouldn't be strictly portable
C any more) and thus here we could have an exit(ALREADY_POSTED_FAILURE),
and then check for that if we cared about the exit status. It's better
to think of the return value not as a "tell me about the status of the
program" but rather as an error code. That is, all programs return
an error code, and thus your test is
if (program_error) { handle_error(); }

and not

if (program_succeded) { do_what_i_wanted_to_do(); }

Now I'm setting myself up for another fall here, since I still haven't
actually caught up on the whole thread.
 
D

David Thompson

Bart van Ingen Schenau <[email protected]> wrote:
The core problem underlying all of this - is the fact that the shell treats
a return value of 0 as "TRUE" and anything else (i.e., non-zero) as FALSE.
Which is the exact opposite of what C (and most other programming languages)

Most programming languages don't treat numbers as boolean in the first
place -- you must compare equal 0, notequal 0, greater-or-equal 0,
whatever. BCPL/B/C/C++ do accept numbers and pointers (in BCPL/B
pointers are integers anyway) -- but not Java which otherwise is
pretty close to C++. FORTH, with semantics close to C but very
different syntax, does.

BASIC, COBOL, FORTRAN, algol, Pascal, Ada, LISP, Python do not. PL/I
kind-of does as a side effect of conversions but it isn't good style.
 
M

Malcolm McLean

Digging further, what underlies that is a basic intuitive feeling
that "TRUE" should be "good" and FALSE "bad", which, I
think, corresponds with most people's basic feelings about the
Universe.
I think that's right.

I distinguish in my thinking between a "function" (unfortunately
the word is also used byte the C standard to mean something
different) and a "procedure". A function is a mapping of input to
output, so fundamentally just shuffles bits about in the computer,
A procedure does something, so performs IO.

A function cannot return an error, unless there is a programming
mistake. If the program contains a mistake, by definition there's no
right behaviour. There's one exception, writes to and from memory
aren't considered IO, so the function can run out of memory (a
Turing machine has an infinite tape, which we can't provide). A
procedure can easily return an error - device not accepting output
or not giving proper input.

Now if we run out of memory, normally we'll signal that by returning
a null pointer. So error condition for a function will usually be
zero. For a procedure, the stdlib convention is to return -1.
 
J

jgharston

BartC said:
I don't use the 0 = Success, Anything Else = Failure pattern much at all,
except when an external API function happens to use it, or when 0 is part of
the data-range being returned.

But then, how do you known /what/ failure has happened? Have you ever
wondered *why* those external APIs you refer to use 0=Ok? There is
only *one* way something can succeed, and an infinite number of ways
something can fail. The only way that can map that to the number
system is for Ok to match the only number there is one instance of -
zero - and for failure to match to the set of numbers there is an
infinite supply of - non-zero.

If zero means failure, how do you know what the failure is so you can
deal with it? Eg, off the top of my head:

if((result=do_something()) {
switch(result) {
case 1: /* crc error, fix it and retry */
case 2: /* read-only, abort if writing */
case 3: /* address not found, abort */
case 4: /* device not ready, pause and retry */
case 5: /* track not found, seek to track zero and retry */
}

JGH
 
N

Nobody

Most programming languages don't treat numbers as boolean in the first
place -- you must compare equal 0, notequal 0, greater-or-equal 0,
whatever. BCPL/B/C/C++ do accept numbers and pointers (in BCPL/B pointers
are integers anyway) -- but not Java which otherwise is pretty close to
C++. FORTH, with semantics close to C but very different syntax, does.

BASIC, COBOL, FORTRAN, algol, Pascal, Ada, LISP, Python do not. PL/I
kind-of does as a side effect of conversions but it isn't good style.

Lisp treats everything as a boolean, but its sole false value is "nil" aka
the empty list, while everything else (including integer zero) is true.

Python has a distinct boolean type but also treats a lot of other things
as boolean: integers, floats, anything with a length (zero-length is
false, non-zero length is true), plus any class with a __nonzero__
method. For any user-defined class lacking __len__ and __nonzero__
methods, all instances are true. The distingiushed None value is false.
 
J

James Kuyper

But then, how do you known /what/ failure has happened? Have you ever
wondered *why* those external APIs you refer to use 0=Ok? There is
only *one* way something can succeed, and an infinite number of ways
something can fail. ...

Perhaps, but there's usually only a finite, and usually small, number of
distinguishable ways to detect the failure of a function. An 'int' is
usually more than sufficient for that purpose.
... The only way that can map that to the number
system is for Ok to match the only number there is one instance of -

Is there any number with more than one instance?
zero - and for failure to match to the set of numbers there is an
infinite supply of - non-zero.

You're comparing a number to a set of numbers, and there's nothing
unique about the role 0 plays in that comparison. You could say the same
about 42 and not-42. Also, there's only a finite "supply" of values that
any actual C function can return.
If zero means failure, how do you know what the failure is so you can
deal with it? Eg, off the top of my head:

if((result=do_something()) {
switch(result) {
case 1: /* crc error, fix it and retry */
case 2: /* read-only, abort if writing */
case 3: /* address not found, abort */
case 4: /* device not ready, pause and retry */
case 5: /* track not found, seek to track zero and retry */
}

One simple-minded approach is to treat all the failure modes the same,
and within limits, it's an entirely workable strategy - it's consistent
with Bart's style that he would use that strategy.
 
K

Kenny McCormack

But then, how do you known /what/ failure has happened? Have you ever
wondered *why* those external APIs you refer to use 0=Ok? There is

Actually, the answer to your (implied) question is mostly: "historical".

If we had it to do all over again, then the best (IMHO, of course) way to do
every single API-type function is like this:

zero_or_one_t do_whatever(int *status, whatever *ret, ...)

Where the returned value (zero_or_one_t) is actually an int, but only ever
returns 0 or 1. The variable "status" returns an error code (usually, of
course, only when the return value indicates failure, but this need not be
universally true), the "whatever *" returns what would have been the normal
function return value, and so on...

P.S. I suspect that this proposal will seem way too IBM-y and/or Microsoft-y
for the Unix-heads in this newsgroup.

--
Windows 95 n. (Win-doze): A 32 bit extension to a 16 bit user interface for
an 8 bit operating system based on a 4 bit architecture from a 2 bit company
that can't stand 1 bit of competition.

Modern day upgrade --> Windows XP Professional x64: Windows is now a 64 bit
tweak of a 32 bit extension to a 16 bit user interface for an 8 bit
operating system based on a 4 bit architecture from a 2 bit company that
can't stand 1 bit of competition.
 
S

Shao Miller

Actually, the answer to your (implied) question is mostly: "historical".

If we had it to do all over again, then the best (IMHO, of course) way to do
every single API-type function is like this:

zero_or_one_t do_whatever(int *status, whatever *ret, ...)

Where the returned value (zero_or_one_t) is actually an int, but only ever
returns 0 or 1. The variable "status" returns an error code (usually, of
course, only when the return value indicates failure, but this need not be
universally true), the "whatever *" returns what would have been the normal
function return value, and so on...

P.S. I suspect that this proposal will seem way too IBM-y and/or Microsoft-y
for the Unix-heads in this newsgroup.

It doesn't seem particularly Microsofty, to me. As mentioned
elsethread, Windows driver development API tend to return an 'NTSTATUS'
type, and helper macros can translate it to the boolean cases.

But your strategy has some redundancy, as '*status' carries some of the
same information as the return value, doesn't it? What makes that
attractive, exactly?
 
M

Mark Storkamp

Robert Wessel said:
Compilers are a classic example, where "successfully compiled with
warnings" is a common condition distinct from "compiled without
warnings".

But gcc, and I suspect most compilers, handle that with an option such
as -Werror that causes warnings to be treated as errors. If you are
using a shell script to compile, and are concerned about warnings, you
would use such an option.
 
G

glen herrmannsfeldt

Robert Wessel said:
On Thu, 28 Feb 2013 08:50:48 -0600, Mark Storkamp
(snip)
FSVO "most". Plenty of non-*nix systems use a multi-level scheme. It
allows your execution script to accumulate warnings, for example.

OS/360 compilers, and I believe still for z/OS use a multi-level system.
You can then set using JCL parameters whether you want to continue on
to link and go steps, or not.

The usual return codes are multiples of four, possibly for convenience
in using them for jump tables. (At least that is the only reason I know
of.)

So, 0 for success, 4 for warning, 8 for error, 12 for fatal error,
and even 16 for something worse.

Unable to open SYSPRINT (what unix calls stdout) usually generates 16,
as there is no place to print a message telling you why it didn't work.

Then there is the return code from the link step, which you can also
change the parameter for, and run even when link failed. You might
want to run, even though there is an undefined external reference
(favorite reason for link failing).

So, I often use other codes in my C programs, such that the user can
figure out why it failed. Yes, especially useful in scripts.

-- glen
 
B

BartC

jgharston said:
But then, how do you known /what/ failure has happened? Have you ever
wondered *why* those external APIs you refer to use 0=Ok? There is
only *one* way something can succeed, and an infinite number of ways
something can fail.

malloc() and fopen() are counter-examples.

The latter might fail for some of the same reasons as in your following
example; has the lack of a full error status causes many problems?
 
N

Nick Keighley

On 02/16/2013 05:46 PM, glen herrmannsfeldt wrote:
...


_Bool b = 3;

The resulting value of 'b' does not quite fit your description.

perhaps he meant booleans are an integral type?
 

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,731
Messages
2,569,432
Members
44,832
Latest member
GlennSmall

Latest Threads

Top