function prototype / method declaration enhancement - assertions

Discussion in 'C++' started by Bhushit Joshipura, Jan 5, 2004.

  1. I would like to gather views about an enhancement in C and C++ that
    can lead to more robust programming.

    First things first:
    1. This posting addresses to both, C and C++. Please map an example to
    your language of concern if it does not belong to your language of
    concern.
    2. Whatever I write here is a clear syntax error in both the
    languages.

    Pre-conditions and post-conditions to a function (method) are not only
    important to theory or to comment inside code. They reflect in terms
    of assert statements also. I propose to formalize them with prototype
    / declaration.

    The idea:

    1. Extend the concept of declaration (prototype) to a step further -
    alongwith parameters, we specify assertions about them too. When the
    function gets called (method gets invoked) these assertions, if
    present, are [optionally] invoked before hitting the body of the
    function.

    For example, in C,

    Current declaration:
    int f ( int i, int *j, int k);

    Possible extension 1:
    int f (
    int i {
    i > 0 && i < INT_MAX
    },
    int *j {
    j != NULL
    },
    int k {
    k != some_global
    }
    );

    2. Extend the concept of declaration to one more step further - we let
    users add one more pair of parantheses in declaration to assert about
    return value. This will complete the design as a communication to the
    user who links the function (method) to her own code as well as will
    generate a robust code.

    For example, in C,

    Current Declaration:
    int *g (int i, int *j, int k);

    Possible Extension 2:
    int *g (
    int i {
    i > 0 && i < INT_MAX
    },
    int *j {
    j != NULL
    },
    int k {
    k != some_global
    }
    ) (
    j != NULL && __RETURN_VALUE__ != NULL
    );

    Advantages:
    1. Clear communication to the person who links her code
    2. Robust programming
    3. Definitions (function bodies) become more readable
    4. Assertions become more maintainable

    Issues:
    3.1 Can compare with constants and globals only. Those should be
    visible in the .h file
    3.2 As the standard does not (and probably will not) impose order of
    evaluation of argument, we can not use one argument in assertion of
    another argument. (We can not default to another argument in C++ for
    the same reason, I guess.)
    3.3 In a sense it violates WYSIWYG nature of C. No problem with C++
    3.4 It may slow down execution. Compilers should provide a switch to
    bypass assertions
    3.5 Necessitates some error handler for assertion failure. (A good
    code must already linking with some sort of <assert.h> or equivalent.)
    3.6 Recursive call in an assertion (calling f in assertion of f's
    argument) and similar stuff
     
    Bhushit Joshipura, Jan 5, 2004
    #1
    1. Advertisements

  2. EventHelix.com, Jan 5, 2004
    #2
    1. Advertisements

  3. Bhushit Joshipura

    puppet_sock Guest

    Hum. While it might be possible to put this in, it could not
    be checked at compile time, at least not in general. You can't
    always pre-compute the call values of functions. You can't even
    pre-compute whether a function will actually be called.
    So, these checks might be created, but they could only actually
    be tested against at run time. The same is true for checking
    return values.
    Well, maybe. It does not necessarily happen that it's any
    clearer to the client coder. I've always found that a good
    set of documentation is worth a lot.

    What happens if the user tweaks the .h file to change the limit?
    Or even removes the limit, expecting to be able to get a log
    function to take negative values. Might be undefined behaviour.
    Possibly. Though it seems like it's just moving testing from
    inside the code to insde the function call. It may make it a
    tad more visible, and make documentation slightly more
    automatic.
    What happens if one of those constants gets changed?
    What is "WYSIWYG nature of C?" I've always found that C has a
    "WYGIWYG nature." That is, "what you get is what you get."
    I should think it would throw an exception in C++.
    Socks
     
    puppet_sock, Jan 5, 2004
    #3
  4. Bhushit Joshipura

    Howard Guest

    Clear? Looks like a nightmare to me. I'd have a hard time seeing quickly
    what parameters a function needs. You can already put whatever assertion
    you like in the body of the function. Why put them in the declaration? And
    good documentation provides the "contract" just as well, and in plainer
    language.
    Meaning what? How are they more robust than using assertions in the
    function body?
    Why? What's being removed from the function body? A few assertions at the
    start (if you even use those in the first place)? I think they're much more
    readable in the function body than mingled with the parameter list.
    Why is the header file more maintainable? I would argue the opposite.
    Changes in the function body, when stored in an implementation file instead
    of a header file, require far less compilation work than changes to a
    header, which can force massive recompilations.
    Not sure what you mean here. WYSIWYG usually refers to what you "see", as
    in a graphical layout tool that produces the results exactly as you design
    them (which, if you've ever used it, VC++ does NOT).
    Definitely slows down execution. Assertions are used for debug builds, and
    turned off for release builds. But you often want to write code that checks
    value ranges, especially for parameters, which does NOT get removed for the
    release build. Totally your decision which to use where, but I still think
    both types belong in the function body, which is where the details of
    implementation exist.
    Recursive calls? Kind of violates you earlier principle that you can only
    check against constants, doesn't it?

    I sincerely doubt you'll see much support for this. It's kind of messy, and
    doesn't really provide much we don't have already. Think of all the
    existing compilers that would be unable to read it, too!

    -Howard
     
    Howard, Jan 5, 2004
    #4
  5. Bhushit Joshipura

    jeffc Guest

    Because client programmers often cannot see the function body. (This is not
    an endorsement of the OP's method.)
     
    jeffc, Jan 5, 2004
    #5
  6. Bhushit Joshipura

    Howard Guest

    If it's only for the client programmer, than it belongs in documentation,
    not code, IMO. The function header defines the parameters for use by the
    compiler, not by the programmer. Proper documentation is what is needed for
    the client programmer to do his/her job correctly.

    (Now, while understanding you're not advocating the OP's position...)

    Aside from exposing details best left to either the implementation or to
    documentation, it is incomplete. How do you specify, for example,
    pre-condtions that do not meet his simple test examples, such as "you must
    first have a valid FTP connection", or "if member A of the passed reference
    parameter is non-zero, then the B member struct must be filled out with
    appropriate values"? If you can't solve the whole problem, why make things
    worsse just to slve a tiny part of it?

    -Howard
     
    Howard, Jan 5, 2004
    #6
  7. Bhushit Joshipura

    Eric Sosman Guest

    Compiler-digestible assertions could have utility beyond
    just debugging. For example, if the compiler can be made to
    understand that a function argument is not merely an `int' but
    a non-negative `int', optimizations like replacing divisions
    with shifts might be applicable. If the compiler can also be
    told that the `int' is in the range [0..9], say, it could omit
    some needless range-checking code in

    switch (zero_through_nine) {
    case 0: ... ; break;
    case 1: ... ; break;
    case 2: ... ; break;
    case 3: ... ; break;
    case 4: ... ; break;
    case 5: ... ; break;
    case 6: ... ; break;
    case 7: ... ; break;
    case 8: ... ; break;
    case 9: ... ; break;
    }

    However, I'm not convinced that the proposed mechanism would
    be expressive enough to enable optimizations that would repay
    the added burdens. I'm sure I'd find myself wanting to say
    that the function `int kbhit()' "almost always" returns zero,
    for example.
     
    Eric Sosman, Jan 5, 2004
    #7
  8. I think we should name this feature.
    in C: assertive prototype
    in C++: assertive declaration
    You are correct. It *is* meant for runtime. It has to be. We are going
    to assert against actual parameters.
    It would not change the behavior because the function / method was
    compiled with original set of values. However, this may throw things
    out of sync. Does not the same happen in case of header files now?
    .... and code slightly more robust. We are discussing this years after
    a little step of introduction of prototypes to C was taken. That too
    improved life a little. Tomorrow comes yet another idea in some brain
    and we will have one more step towards better life.
    Constants won't change (or they would be variables!:). Globals may
    change.

    The very fact that the assertive declaration uses a global, implies
    that it must depend on temporal value of the global. So (barring race
    conditions), anything referring to a global will (luckily) change
    behavior with the value of that global.
    You are correct again. C is highly (and perhaps uniquely) WYSIWYG.

    Assertive prototypes sort of violate that nature because assertions
    won't be "seen in .c file". Implementation will happen "out of the
    body of function".

    However, assertive prototypes will still be highly predictable because
    they actually steal the code from function body and put before (or
    after) the body.
    -Bhushit
     
    Bhushit Joshipura, Jan 5, 2004
    #8
  9. Exactly.
    And in fact, many functions have prerequisites for the data
    structures with which they operate that would need much more
    extensive validation than a simple range check.

    The history of programming is full of examples of people
    trying to automate correctness by linguistic constructs,
    and Lo! they haven't succeeded. It's the wrong approach.
     
    Douglas A. Gwyn, Jan 6, 2004
    #9
  10. True. However, that belongs in the function body, not in the
    declaration. Then the assertions can also be used about other
    variables, e.g. to assert loop invariants.

    Assertions about the return value from the function belong in
    the declaration, however.
    One could support both __Expect(return == 0) which describes the
    usual return value, and __Assert(return >= 0) which describes all
    return values.
     
    Hallvard B Furuseth, Jan 6, 2004
    #10
  11. Bhushit Joshipura

    jeffc Guest

    Header code *is* proper documentation.
    I don't think it's a very good solution.
     
    jeffc, Jan 6, 2004
    #11
  12. Bhushit Joshipura

    Eric Sosman Guest

    Damn straight! Who could read

    int sprintf(char * restrict,
    const char * restrict, ...);

    .... and fail to understand all that is needful? ;-)
     
    Eric Sosman, Jan 6, 2004
    #12
  13. I think the following should be made clear:

    1. assertive declarations are not in lieu of asserts.
    For example, if there is an assertion of memory allocation or a socket
    creation in a local variable, it has no where to go but in definition.

    2. assertive declarations are not compulsory.
    So existing code and compilers will work perfectly with new
    conditions. If there is some problem, we can tune the proposed syntax.

    3. documentation costs money (or time or both) - at both ends.
    Provider has to code and write the documentation (notice gap? There
    may be two separate departments at work! Engineer writes the code,
    tech writer documents what is out there. Engineer's time is to be
    scheduled for documentation for which management is seldom happy).
    Client has to read the documentation and code (.h files). The more the
    number of serialized channels, the less the capacity.

    4. Not everyone uses doxygen or doc++. In fact, at most places,
    "well-documented" code is only for the out-going product and not
    within modules. This lack of internal communication is very very
    expensive but seldom accounted for.

    5. As someone pointed out in the thread, outgoing code too needs
    assertions. For the same purpose, another pair of parantheses in
    declaration is suggested.

    6. Upon programmer's decision, these assertive declarations may be
    turned into executables statements or not. So in the "released" code,
    just the periphery of code may have assertions and speed would not be
    compromised much.

    -Bhushit



     
    Bhushit Joshipura, Jan 6, 2004
    #13
  14. 1. You certainly have a lot of practice of reading code. So you tend
    to find what is familiar - and this is not. So it may look like a
    nightmare for a while :)

    Let me be serious. This is just a proposed syntax and we all can
    change it to make it more readable.

    2. Yes, but how many of us have access to code where assertions are
    put today? While proposed method does not put *all* assertions in
    client's view, it puts all those in which client may be interested in
    view of client.

    3. Documentation adds to channel (please refer to my other posting in
    this thread), so noise and so loss of information and synchronization
    problems and cost of software.

    4. Plainer language is not more precise.

    Don't you think it is good programming practice to separate argument
    related assertions from variable related assertions? Will this not
    make life easier?

    [snip]
    1. Let us not confuse between maintenance and compilation time.
    2. Here is a clear line drawn between assertion about input and
    assertion about internal conditions. There will be a clear before
    *before* function body execution. That will clearly indicate the
    violation of precondition (or *after* the function body indicating
    violation of postcondition).
    3. Recompilation is not bad if a component changes its interface or
    assumptions about it. A day's recompilation is better than a week's
    bug hunt.

    [snip]
    Please read again. I am not attributing WYSIWYG to C++ (and will never
    do so for VC++).

    C is very clear to its readers about its translation and execution to
    an informed reader. So I said "WYSIWYG nature of C". It is harder to
    sell in-built assertion to C crowd because their function call
    mechanism will be unclear.

    C++ is little muddy. That makes assertive declarations an advantage in
    C++. C++ already provides default assignments - so argument assertions
    seem to go along. [It may be much more difficult to achieve, though.]
    Once again, this proposal is trying to achieve a distinction between
    assumptions and algorithm but does not enforce it. As you point out
    correctly, anything that can not be done in .h, will have to be done
    in .c(pp).
    Please read again. We can check against not only constants but globals
    also. Functions in C are viewable to all and a method is viewable from
    its class in C++ so a recursive assertive declaration is a legitimate
    concern.
    1. Good changes may change only a little bit :)
    2. We have re-written compilers for changes in languages. Did not we?
    Prototypes were not there since the beginning of K&R C. Structures
    were not passed by value in K&R C. ANSI C does all that. Old compilers
    are on their way to graveyard anyway. New code will have to work with
    new compilers.
    New compilers will have to understand old code - for applications have
    to live. This feature being non-compulsive, they will anyway
    understand non-assertive declarations.

    -Bhushit
     
    Bhushit Joshipura, Jan 6, 2004
    #14
  15. Bhushit Joshipura

    jeffc Guest

    What I mean is that it documents what it documents. I'm not saying other
    comments aren't required or useful. There is no need, for example, to
    document the fact that the name of the function is sprintf, or the fact that
    it returns an int. Or to put it another way, those things are already
    clearly documented.
     
    jeffc, Jan 6, 2004
    #15
  16. Thanks.

    I went through Eiffel and the link you provided.
    Unfortunately that too does not make contract visible to the client
    (in .h file).
    My suggestion is to make contract visible to client of the code. In
    that way, we
    1. avoid documentation overhead
    2. communicate to human beings better
    3. make maintenance more modular
    4. absorb design by contract philosophy

    One of my friends, Taral Oza, suggested to change syntax to the
    following one. The advantage of his syntax were
    1. precondition and postcondition are just executable statements, no
    special syntax is necessary
    2. We just have to prepend / append them to the function body
    3. inter-relations among arguments can be asserted

    Here is hwo

    int C::f ( int i, int j, int *k)
    { // precondition block
    assert (i != 0 );
    assert (j < i);
    assert (k != NULL);
    }
    { // postcondition block
    assert (k != NULL);
    assert __RETURN_VALUE__ != 0
    }; // end of *declaration*
     
    Bhushit Joshipura, Jan 13, 2004
    #16
  17. Bhushit Joshipura

    Dave Hansen Guest

    On 13 Jan 2004 14:17:40 -0800, (Bhushit Joshipura)
    wrote:

    [...]
    Good, you're almost there. Just add a couple more braces:
    } // And the last brace here, and you're done

    Regards,

    -=Dave
     
    Dave Hansen, Jan 13, 2004
    #17
    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.