exit()

Discussion in 'C Programming' started by BartC, Feb 14, 2013.

  1. BartC

    BartC Guest

    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.
     
    BartC, Feb 17, 2013
    #21
    1. Advertisements

  2. BartC

    Shao Miller Guest

    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.
     
    Shao Miller, Feb 17, 2013
    #22
    1. Advertisements

  3. 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).

    [...]
    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?
     
    Keith Thompson, Feb 17, 2013
    #23
  4. BartC

    BartC Guest

    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.
     
    BartC, Feb 17, 2013
    #24
  5. 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.
    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);
    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).
     
    Keith Thompson, Feb 17, 2013
    #25
  6. BartC

    Jorgen Grahn Guest

    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
     
    Jorgen Grahn, Feb 18, 2013
    #26
  7. "Happy families are all alike; every unhappy family is unhappy in its
    own way."

    And with that Tolstoy anticipates the unix architecture.
     
    Nathan Wagner, Feb 22, 2013
    #27
  8. Message-ID: <>
    Great minds think alike?
     
    Ben Bacarisse, Feb 23, 2013
    #28
  9. 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.
     
    Nathan Wagner, Feb 23, 2013
    #29
  10. 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.
     
    David Thompson, Feb 24, 2013
    #30
  11. 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.
     
    Malcolm McLean, Feb 24, 2013
    #31
  12. BartC

    jgharston Guest

    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
     
    jgharston, Feb 25, 2013
    #32
  13. BartC

    Nobody Guest

    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.
     
    Nobody, Feb 25, 2013
    #33
  14. BartC

    James Kuyper Guest

    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.
    Is there any number with more than one instance?
    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.
    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.
     
    James Kuyper, Feb 25, 2013
    #34
  15. 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.
     
    Kenny McCormack, Feb 25, 2013
    #35
  16. BartC

    Shao Miller Guest

    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?
     
    Shao Miller, Feb 25, 2013
    #36
  17. 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.
     
    Mark Storkamp, Feb 28, 2013
    #37
  18. 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
     
    glen herrmannsfeldt, Mar 1, 2013
    #38
  19. BartC

    BartC Guest

    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?
     
    BartC, Mar 4, 2013
    #39
  20. perhaps he meant booleans are an integral type?
     
    Nick Keighley, Mar 9, 2013
    #40
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.