B
Bhushit Joshipura
This post contains one question and one proposal.
A. May I know why order of evaluation of arguments is not specified in
C/C++?
I asked a question in comp.lang.c++ for the following possibility and
because the languages do not specify the order of evaluation, doing so
was an error.
int B::f ( int i, int j = i + 1 ) { // j defaults to i + 1
....
}
(Folks there suggested either to use polymorphism or to call
constructor of another class.)
B. If such an order is specified, we can not only default an argument
based on previous arguments, we can declare methods / functions
better. Think of this:
// file foo.h
// This example gains from fixed order of evaluation of arguments
// but it is not necessary to have fixed order if we disallow
references to
// previous arguments from an argument
(int *) B::f (
int i { //document here and assert at run time
i > 0 && i < INT_MAX
},
int j = i + 1 {
j > i || j < 0
},
int *k {
k != NULL && k!= some_global
}
) // end of argument documented assertion now return value's
turn
(
!= NULL
);
Upon execution, prologue to B::f may take each argument and assert.
The first argument can be asserted only against constants and globals.
If order of evaluation is not specified, other arguments follow the
suite
else their assertions and default assignment if any, may include
previous argument.
Then B::f's body gets executed
Then B::f's return value (if any) is checked against assertions if
any.
[I feel shy of suggesting a "default return value" for a function.]
This scheme has three advantages:
1. It facilitates communicating pre-conditions, post-conditions very
clearly (BIG plus)
2. It makes definition of function f clearer
3. Splint and other lints depend on specially annotated code to write
assumptions about the code. This approach is replacing annotation with
assertion and making code more robust.
I can see five problems with this scheme - some of them are easy to
solve
1. It may not always be possible to resolve globals and externs in a
..h file.
2. It may come in way of "counting the clock cycles" styled C because
of hidden assertions. [ A good code must have those assertions anyway.
#3 below solves this.]
3. These assertions must be "bypass-able" for released code. [However,
provision of an option in compiler would do that. Even then, all
argument related assertions are now viewable at a glance in a .h
file.]
4. It makes necessary to link the code to <assert.h> or equivalent.
[Once again, a good code must have this anyway.]
5. Function calls within assertions may become nasty. [At the worst,
we can disallow function calls there.]
Your comments?
-Bhushit
A. May I know why order of evaluation of arguments is not specified in
C/C++?
I asked a question in comp.lang.c++ for the following possibility and
because the languages do not specify the order of evaluation, doing so
was an error.
int B::f ( int i, int j = i + 1 ) { // j defaults to i + 1
....
}
(Folks there suggested either to use polymorphism or to call
constructor of another class.)
B. If such an order is specified, we can not only default an argument
based on previous arguments, we can declare methods / functions
better. Think of this:
// file foo.h
// This example gains from fixed order of evaluation of arguments
// but it is not necessary to have fixed order if we disallow
references to
// previous arguments from an argument
(int *) B::f (
int i { //document here and assert at run time
i > 0 && i < INT_MAX
},
int j = i + 1 {
j > i || j < 0
},
int *k {
k != NULL && k!= some_global
}
) // end of argument documented assertion now return value's
turn
(
!= NULL
);
Upon execution, prologue to B::f may take each argument and assert.
The first argument can be asserted only against constants and globals.
If order of evaluation is not specified, other arguments follow the
suite
else their assertions and default assignment if any, may include
previous argument.
Then B::f's body gets executed
Then B::f's return value (if any) is checked against assertions if
any.
[I feel shy of suggesting a "default return value" for a function.]
This scheme has three advantages:
1. It facilitates communicating pre-conditions, post-conditions very
clearly (BIG plus)
2. It makes definition of function f clearer
3. Splint and other lints depend on specially annotated code to write
assumptions about the code. This approach is replacing annotation with
assertion and making code more robust.
I can see five problems with this scheme - some of them are easy to
solve
1. It may not always be possible to resolve globals and externs in a
..h file.
2. It may come in way of "counting the clock cycles" styled C because
of hidden assertions. [ A good code must have those assertions anyway.
#3 below solves this.]
3. These assertions must be "bypass-able" for released code. [However,
provision of an option in compiler would do that. Even then, all
argument related assertions are now viewable at a glance in a .h
file.]
4. It makes necessary to link the code to <assert.h> or equivalent.
[Once again, a good code must have this anyway.]
5. Function calls within assertions may become nasty. [At the worst,
we can disallow function calls there.]
Your comments?
-Bhushit