function prototype / method declaration enhancement - assertions

  • Thread starter Bhushit Joshipura
  • Start date
B

Bhushit Joshipura

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
 
P

puppet_sock

[email protected] (Bhushit Joshipura) wrote in message news: said:
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
}
);

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.
Advantages:
1. Clear communication to the person who links her code

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.
2. Robust programming
3. Definitions (function bodies) become more readable
4. Assertions become more maintainable

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.
Issues:
3.1 Can compare with constants and globals only. Those should be
visible in the .h file

What happens if one of those constants gets changed?
3.3 In a sense it violates WYSIWYG nature of C. No problem with C++

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."
3.5 Necessitates some error handler for assertion failure. (A good
code must already linking with some sort of <assert.h> or equivalent.)

I should think it would throw an exception in C++.
Socks
 
H

Howard

Bhushit Joshipura said:
Advantages:
1. Clear communication to the person who links her code

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.
2. Robust programming

Meaning what? How are they more robust than using assertions in the
function body?
3. Definitions (function bodies) become more readable

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.
4. Assertions become more maintainable

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.
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++

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).
3.4 It may slow down execution. Compilers should provide a switch to
bypass assertions

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

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
 
J

jeffc

Howard said:
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?

Because client programmers often cannot see the function body. (This is not
an endorsement of the OP's method.)
 
H

Howard

jeffc said:
Because client programmers often cannot see the function body. (This is not
an endorsement of the OP's method.)

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
 
E

Eric Sosman

Howard said:
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.

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.
 
B

Bhushit Joshipura

I think we should name this feature.
in C: assertive prototype
in C++: assertive declaration
[snip]
The idea:
[snip]
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.

You are correct. It *is* meant for runtime. It has to be. We are going
to assert against actual parameters.
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.

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?
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.

.... 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.
What happens if one of those constants gets changed?

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.
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."

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.
I should think it would throw an exception in C++.
Socks

-Bhushit
 
D

Douglas A. Gwyn

Howard said:
You can already put whatever assertion
you like in the body of the function.

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.
 
H

Hallvard B Furuseth

Eric said:
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.

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

One could support both __Expect(return == 0) which describes the
usual return value, and __Assert(return >= 0) which describes all
return values.
 
J

jeffc

Howard said:
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.

Header code *is* proper documentation.
(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?

I don't think it's a very good solution.
 
E

Eric Sosman

jeffc said:
Howard said:
[...] Proper documentation is what is needed for
the client programmer to do his/her job correctly.

Header code *is* proper documentation.

Damn straight! Who could read

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

.... and fail to understand all that is needful? ;-)
 
B

Bhushit Joshipura

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



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
 
B

Bhushit Joshipura

Howard said:
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.

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.

Meaning what? How are they more robust than using assertions in the
function body?

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]
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.

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

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.]
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.

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).
Recursive calls? Kind of violates you earlier principle that you can only
check against constants, doesn't it?

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

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
 
J

jeffc

Eric Sosman said:
jeffc said:
Howard said:
[...] Proper documentation is what is needed for
the client programmer to do his/her job correctly.

Header code *is* proper documentation.

Damn straight! Who could read

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

... and fail to understand all that is needful? ;-)

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.
 
B

Bhushit Joshipura

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*
 
D

Dave Hansen

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

[...]
Here is hwo

Good, you're almost there. Just add a couple more braces:
int C::f ( int i, int j, int *k) { // One here
{ // precondition block
assert (i != 0 );
assert (j < i);
assert (k != NULL);
} // Add the function code here
{ // postcondition block
assert (k != NULL);
assert __RETURN_VALUE__ != 0
}; // end of *declaration*
} // And the last brace here, and you're done

Regards,

-=Dave
 

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,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top