Dilemma of const-ifying a function parameter

H

hzmonte

Correct me if I am wrong, declaring formal parameters of functions as
const, if they should not be/is not changed, has 2 benefits;
1. It tells the program that calls this function that the parameter
will not be changed - so don't worry.
2. It tells the implementor and the maintainer of this function that
the parameter should not be changed inside the function. And it is for
this reason that some people advocate that it is a good idea to
const-ify even a non-pointer parameter:
void my_func(const int i) { ... }
because if the original programmer or a subsequent maintainer
accidentally change the value of the parameter, the compiler can catch
it.
A third benefit may be that the compiler can optimize the program if it
knows for sure that the parameter is not changed inside the function. I
am not sure about this though.
Now, I would like to benefit from the const-ification of the function
parameter. Let's say I have this code:

typedef struct s_fifoItem t_fifoItem;
struct s_fifoItem {
t_fifoItem *next;
t_fifoItem *prev;
void *elem;
};
typedef t_fifoItem t_fifoQueue;

void enqueue(t_fifoQueue *q, void *elem)
{
t_fifoItem *i, *last;

i = malloc(sizeof(t_fifoItem));
i->elem = elem;
last = q->prev;
last->next = i;
i->next = q;
q->prev = i;
}

elem appears to be a good candidate for const-ification. Intutitively
the program calling enqueue() normally does not expect that the item to
be enqueued is to be changed. And the implementor of enqueue() would
not want to change elem either; and actually elem is not changed in the
above implementation. But if I const-ify elem, gcc would complain (in a
warning) that the assignment
i->elem = elem;
would discard the const qualifier because the type of "i->elem" is
"void *", not "const void *". One solution is to cast away the
const-ness of elem in that assignment:
i->elem = (void *)elem;
Another solution is to change the declaration of elem in struct
s_fifoItem to be:
const void *elem;
The latter solution is not practical because there are other places
that will change *elem.
However, the former solution of casting away the const-ness is not very
appealing. It is kind of cheating.
So, how do people resolve this? And to a deeper level, gcc seems to be
caught bewteen 2 conflicting goals: ensuring type correctness (a double
cannot be assigned to an int, a const void * cannot be assigned to a
void *, etc) and ensuring the unmodifiability of a variable. From the
point of view of language design, what are the issues involved and how
do other programming languages deal with it?

P.S. I know nothing about C++. But in its queue STL, there is a
function
void push(const T &val)
The const here means that the element is a const parameter. This
confirms my belief that it is good programming practice to const-ify
the element to be enqueued. But I wonder how this const-ification is
implemented inside push(). It is possible that the parameter is made
constant in the prototype from the client's perspective (to reap the
first benefit I mentioned above) but the parameter is cast (void *)
inside the function (so the second benefit is not reaped, and the STL
coders (i.e. ones who code the STL) need to be careful).
 
M

Michael Mair

Correct me if I am wrong, declaring formal parameters of functions as
const, if they should not be/is not changed, has 2 benefits;
1. It tells the program that calls this function that the parameter
will not be changed - so don't worry.
2. It tells the implementor and the maintainer of this function that
the parameter should not be changed inside the function. And it is for
this reason that some people advocate that it is a good idea to
const-ify even a non-pointer parameter:
void my_func(const int i) { ... }

Here you are going wrong.
There is a conceptual difference between
void my_pfunc (const int *pI);
and
void my_func (const int i);
For the former, whatever pI points to must not be modified
but pI may be modified. The equivalent of the latter is
void my_pfunc2 (int * const pI);
which does not allow to modify the _value_ of the parameter.
As the change of the parameter value has no bearing whatsoever
on the argument being passed, there is indeed only the local
"benefit" of not being able to change the parameter.
This IMO is more a question of style than anything else.
If you want to do that consequently and have benefit 1 and 2
on top of it, you arrive at
const Type
<some number of "* const" and plain "*" in between>
ParameterName
i.e. you will have
const int * const pI
for the above and
const int ** const ppI
or
const int * const * const ppI
to be safe on either the level of the values at the bottom
and level of the pointer value or on all levels.

because if the original programmer or a subsequent maintainer
accidentally change the value of the parameter, the compiler can catch
it.
A third benefit may be that the compiler can optimize the program if it
knows for sure that the parameter is not changed inside the function. I
am not sure about this though.
Now, I would like to benefit from the const-ification of the function
parameter. Let's say I have this code:

typedef struct s_fifoItem t_fifoItem;
struct s_fifoItem {
t_fifoItem *next;
t_fifoItem *prev;
void *elem;
[*]

};
typedef t_fifoItem t_fifoQueue;

void enqueue(t_fifoQueue *q, void *elem)
{
t_fifoItem *i, *last;

i = malloc(sizeof(t_fifoItem));

You forgot to check the return value of malloc().
Undefined behaviour from here.
Note that
i = malloc(sizeof *i);
is safer in that you have not to adapt the line if the
type of i changes (e.g. copy&paste+adaption).
i->elem = elem;

Make [*]
const void *elem
if you want to never ever modify *elem via i->elem
and never to be able to modify *(i->elem) when you get
the address from i->elem instead of another copy of the
address.
Then "const"ing *elem via "const void *elem" makes sense.
This is only the case if you want to store the address for
comparisons of either address or value pointed to.
last = q->prev;
last->next = i;
i->next = q;
q->prev = i;
}

elem appears to be a good candidate for const-ification. Intutitively
the program calling enqueue() normally does not expect that the item to
be enqueued is to be changed. And the implementor of enqueue() would
not want to change elem either; and actually elem is not changed in the
above implementation. But if I const-ify elem, gcc would complain (in a
warning) that the assignment
i->elem = elem;
would discard the const qualifier because the type of "i->elem" is
"void *", not "const void *". One solution is to cast away the
const-ness of elem in that assignment:
i->elem = (void *)elem;
Another solution is to change the declaration of elem in struct
s_fifoItem to be:
const void *elem;
The latter solution is not practical because there are other places
that will change *elem.
However, the former solution of casting away the const-ness is not very
appealing. It is kind of cheating.

There is a difference between "the pointer parameter will not be
used in the function to modify what the pointer points to" and
"the address passed via the pointer parameter and its copies made
during the function's lifetime are not used to modify what the
pointer points to". You essentially promise the latter when saying
"const void *elem".
Once again: const-ifying elem means "void * const elem" and that
the parameter will not change its value. If you want to be able
to say "the value elem points to is not changed but the value
i->elem points to may be changed" then you have also to pass the
size of *elem and create a copy of *elem:
i->elem = malloc(size);
if (i->elem == NULL) {
/* Your sensible action here */
}
memcpy(i->elem, elem, size);
So, how do people resolve this? And to a deeper level, gcc seems to be
caught bewteen 2 conflicting goals: ensuring type correctness (a double
cannot be assigned to an int, a const void * cannot be assigned to a
void *, etc) and ensuring the unmodifiability of a variable. From the
point of view of language design, what are the issues involved and how
do other programming languages deal with it?

gcc just does the thing the language is promising.

P.S. I know nothing about C++. But in its queue STL, there is a
function
void push(const T &val)
The const here means that the element is a const parameter. This
confirms my belief that it is good programming practice to const-ify
the element to be enqueued. But I wonder how this const-ification is
implemented inside push(). It is possible that the parameter is made
constant in the prototype from the client's perspective (to reap the
first benefit I mentioned above) but the parameter is cast (void *)
inside the function (so the second benefit is not reaped, and the STL
coders (i.e. ones who code the STL) need to be careful).

Note that for C++ templates, the type of the passed val is known,
thus a local copy can be made, just as I suggested above.
If the queue stored a "const T *" or "const T&" copy of val, then
you have once again a pointer or reference to dVal=(whatever
dereferencing val gives you) that cannot be used to change dVal.
If you change whatever is stored in the queue, then you have no
changes whatsoever to dVal.

This is just the same as giving &dElem to enqueue() -- the
prototype of enqueue promises you that dElem will never ever
be changed by the address given to enqueue() or one of its
copies made during the run-time of enqueue(). At least if
enqueue() is honouring the promise of "const void * elem".
</OT>


Cheers
Michael
 
M

Malcolm

elem appears to be a good candidate for const-ification. Intutitively
the program calling enqueue() normally does not expect that the item to
be enqueued is to be changed. And the implementor of enqueue() would
not want to change elem either; and actually elem is not changed in the
above implementation. But if I const-ify elem, gcc would complain (in a
warning) that the assignment
i->elem = elem;
would discard the const qualifier because the type of "i->elem" is
"void *", not "const void *". One solution is to cast away the
const-ness of elem in that assignment:
i->elem = (void *)elem;
Another solution is to change the declaration of elem in struct
s_fifoItem to be:
const void *elem;
The latter solution is not practical because there are other places
that will change *elem.
What you are describing is const poisoning. Very few variables, by
definition, are truly constant through the life of the program. However they
may be constant for a defined period of time.
const was added to C by a committee. Committees are good at taking certain
types of decisions, but language design isn't one of them. A good language
is more like a Renasissance masterpiece than a ladies' knitting circle tea
rota. It doesn't express the constness of data very well.
The question is, what to do about it? There is no good answer. If you reject
const algoether you risk making code uncallable by those folish enough to
use it, if you use it you get these types of problems.
 
B

Bill Pursell

Correct me if I am wrong, declaring formal parameters of functions as
const, if they should not be/is not changed, has 2 benefits;
1. It tells the program that calls this function that the parameter
will not be changed - so don't worry.
2. It tells the implementor and the maintainer of this function that
the parameter should not be changed inside the function. And it is for
this reason that some people advocate that it is a good idea to
const-ify even a non-pointer parameter:
void my_func(const int i) { ... }
because if the original programmer or a subsequent maintainer
accidentally change the value of the parameter, the compiler can catch
it.

FWIW, I don't ever see the point of doing this. If you want
that type of behavior, it's much more reasonable to do:
void my_func(int i) { const int i_copy = i; ...}. If the problem
is something like:
void my_func(len I)
{
for ( ; len > 0 ; len--)
;
/* oops, future maintainer thinks len is still valid */
},

then making len const just forces you to add a loop
variable and the new code becomes the above
suggestion, with the const transposed. Usually, when
I see foo(const int arg) I take it as a clue that the
author is a newbie and it raises warning flags.
A third benefit may be that the compiler can optimize the program if it
knows for sure that the parameter is not changed inside the function. I
am not sure about this though.

The restrict keyword is probably more beneficial for optimization,
but I believe const has an effect as well. (I have no empirical
evidence to support this claim.)

One solution is to cast away the
const-ness of elem in that assignment:
i->elem = (void *)elem;
Another solution is to change the declaration of elem in struct
s_fifoItem to be:
const void *elem;
The latter solution is not practical because there are other places
that will change *elem.

I would go with the former, but a slight modification
of the second solution is to provide an accessor function
which casts away the const from the structure. eg
void *ret;
ret = (void *)(i->elem)
This has essentially exactly the same problems you
describe, but it localizes the cast.
However, the former solution of casting away the const-ness is not very
appealing. It is kind of cheating.
So, how do people resolve this?

I've been wondering about this for some time, and I'm starting
to lean towards using const less often than I'd like. For example,
consider an implementation of strchr:
error_t my_strchr(const char *string, char target, const char
**location);
Where instead of returning the index, you put a pointer to the
first occurence of target in *location. If you don't constify
location, you'll need to cast its assignment in my_strchr. If you
do constify it, then the caller must always pass consts: ie to
replace 'a' with 'b', you'd like to do:

while (my_strchr( string, 'a', &location)) {
*location = 'b';
}

but instead you'll need to cast away the const of location. Again,
I think the solution here is to cast the assignment within my_strchr.
It's aesthically unappealing, but IMO it's the correct thing to do.
 
M

Michal Nazarewicz

A third benefit may be that the compiler can optimize the program if it
knows for sure that the parameter is not changed inside the function. I
am not sure about this though.

No.

#include <stdio.h>
void foo(const int *a, int *b) {
printf("*a = %d; *b = %d\n", *a, *b);
*b++;
printf("*a = %d; *b = %d\n", *a, *b);
}
int main(void) {
int c = 1;
foo(&c, &c);
return 0;
}


There is no way compiler can tell whether the thing pointed by a const
int* pointer will change or not. You'll had to use 'restrict'
keyword for that:

void foo(const int restrict *a, int restrict *b) { /* ... */

However, still it's up to programmer not compiler to guarantee that
a and b point to different things.
struct s_fifoItem {
t_fifoItem *next;
t_fifoItem *prev;
void *elem;
}; [...]
void enqueue(t_fifoQueue *q, void *elem) {
t_fifoItem *i, *last;
i = malloc(sizeof(t_fifoItem));
i->elem = elem; [...]
}

elem appears to be a good candidate for const-ification.

You'll have to constify it in the structure also and it may or may not
break your code depending on what you do with the pointer.
One solution is to cast away the const-ness of elem in that
assignment:

Then, constifing the formal parameter would be pointless.
Another solution is to change the declaration of elem in struct
s_fifoItem to be:
const void *elem;

And that's what you have to do.
The latter solution is not practical because there are other places
that will change *elem.

So you cannot constify the parameter. The function doesn't change
what elem points to but it doesn't matter. It saves the pointer
somewhere for other functions to be able to modify it so indirectly
the function is modifying it.
However, the former solution of casting away the const-ness is not very
appealing. It is kind of cheating.

It's stupid and pointless. :)
So, how do people resolve this?

If any other function will modify what elem points to you cannot
constify the argument. Compare this to that code:

void inc(int *bar) { ++*bar; }
void foo(const int *const_bar) { inc((int *)const_bar); }

Your saying: OK, foo() doesn't modify *const_bar so we can constify it
but at the same time inc() modifies it so we need to cast it to
non-const pointer but in the end doesn't foo() end modifying
*const_bar?
P.S. I know nothing about C++. But in its queue STL, there is a
function
void push(const T &val)
The const here means that the element is a const parameter. This
confirms my belief that it is good programming practice to const-ify
the element to be enqueued.

I don't know STL but probably there is also a const T &pop() method
which returns *const* reference or val is being *copied* and then
pop() returns non-const reference to the copy. In your case, you want
a void push(const T *val) and T *pop() functions which is wrong.
It is possible that the parameter is made constant in the prototype
from the client's perspective (to reap the first benefit I mentioned
above) but the parameter is cast (void *)

No.
 
F

Frederick Gotham

hzmonte posted:
Correct me if I am wrong, declaring formal parameters of functions as
const, if they should not be/is not changed, has 2 benefits;


I use the exact same rules for defining function arguments as const, as I
use for defining any object as const.

1. It tells the program that calls this function that the parameter
will not be changed - so don't worry.
2. It tells the implementor and the maintainer of this function that
the parameter should not be changed inside the function. And it is for
this reason that some people advocate that it is a good idea to
const-ify even a non-pointer parameter:
void my_func(const int i) { ... }


Yes, I would define "i" as const if it weren't to be changed.

void enqueue(t_fifoQueue *q, void *elem)
{
t_fifoItem *i, *last;

i = malloc(sizeof(t_fifoItem));
i->elem = elem;
last = q->prev;
last->next = i;
i->next = q;
q->prev = i;
}

elem appears to be a good candidate for const-ification.


Indeed:

void *const elem;

Intutitively the program calling enqueue() normally does not expect that
the item to be enqueued is to be changed. And the implementor of
enqueue() would not want to change elem either; and actually elem is not
changed in the above implementation. But if I const-ify elem, gcc would
complain (in a warning) that the assignment
i->elem = elem;
would discard the const qualifier because the type of "i->elem" is
"void *", not "const void *". One solution is to cast away the
const-ness of elem in that assignment:
i->elem = (void *)elem;


No no no! Leave the function argument as a "pointer to const".

There's a difference between an actual object being const, e.g.:

int const i;
int *const p;

, and a pointer to const:

int const *p;

If you change the function parameter to "pointer to NON-const", then
there's the danger of the following:

char const buf[5] = {0};
Func(buf);

Because the address of the array will eventually be held in a "void*"
(which is a pointer to NON-const), there's the danger of it being altered
at some point.

So, how do people resolve this? And to a deeper level, gcc seems to be
caught bewteen 2 conflicting goals: ensuring type correctness (a double
cannot be assigned to an int, a const void * cannot be assigned to a
void *, etc) and ensuring the unmodifiability of a variable. From the
point of view of language design, what are the issues involved and how
do other programming languages deal with it?


First thing to tackle is this:

Should "s_fifoItem" contain a "void*", or a "void const*". If the
former, then this implies that the pointer will be used to alter the data
at that address. If the latter, this implies that the data won't be
altered. If you choose to leave it as "void*", then make your function take
a "void*".

P.S. I know nothing about C++. But in its queue STL, there is a
function
void push(const T &val)
The const here means that the element is a const parameter.


No it doesn't -- it means that val is a reference to a const T.

(A reference can't be const because it isn't an object -- the following
won't compile: int &const i)
 
H

hzmonte

Here you are going wrong.
Where did I go wrong? I did make a mistake by saying constifying elem;
what I meant was constifying *elem. That is, the data pointed to by
the pointer is not changed.
Make [*]
const void *elem
if you want to never ever modify *elem via i->elem
and never to be able to modify *(i->elem) when you get
the address from i->elem instead of another copy of the
address.
Then "const"ing *elem via "const void *elem" makes sense.
I do want to modify *elem outside this (enqueue) function. That is why
I said I have a dilemma.
There is a difference between "the pointer parameter will not be
used in the function to modify what the pointer points to" and
"the address passed via the pointer parameter and its copies made
during the function's lifetime are not used to modify what the
pointer points to". You essentially promise the latter when saying
"const void *elem".
Yes, I want that (latter) promise.
Once again: const-ifying elem means "void * const elem" and that
Again, I should have said "const-ifying *elem".
the parameter will not change its value. If you want to be able
to say "the value elem points to is not changed but the value
i->elem points to may be changed" then you have also to pass the
size of *elem and create a copy of *elem:
i->elem = malloc(size);
if (i->elem == NULL) {
/* Your sensible action here */
}
memcpy(i->elem, elem, size);
It would be more precise to say that what I want is "the value elem
points to is not changed within the function but the value i->elem
points to may be changed outside the function".
Your suggestion resolved the problem but requires additional memory
that is not necessary had C provided a way to get around this dilemma.
There is no real need to change *elem in the function and thus no need
to malloc a copy of *elem - the need arises only because C does not
satisfy both goals (type compatibility and the desirability to
represent a parameter as "does not change" in a function) at the same
time.
 
H

hzmonte

Michal said:
Then, constifing the formal parameter would be pointless.
It is not pointless. Constifying the formal parameter tells the
calling function that "Hey, don't worry, I will not change *elem within
me." The caller needs not worry about any side effect.
And that's what you have to do.


So you cannot constify the parameter. The function doesn't change
what elem points to but it doesn't matter. It saves the pointer
somewhere for other functions to be able to modify it so indirectly
the function is modifying it.
It matters because constifying a parameter makes a promise that it is
not changed inside the function. Conversely speaking, if the parameter
is not changed inside the function, then it can be const-ified. If the
parameter is not changed inside the function and it still cannot be
const-ified, then what is the use of "const"?
I am inclined to disagreeing to your "indirect modification" argument.
I think "const" promises that the parameter is not modified inside the
function. It is a local promise. My understanding is that it is never
intended to take into consideration whether the parameter would be
modified outside the function, probably as a result of some action
inside the function.
If any other function will modify what elem points to you cannot
constify the argument. Compare this to that code:

void inc(int *bar) { ++*bar; }
void foo(const int *const_bar) { inc((int *)const_bar); }

Your saying: OK, foo() doesn't modify *const_bar so we can constify it
but at the same time inc() modifies it so we need to cast it to
non-const pointer but in the end doesn't foo() end modifying
*const_bar?
The difference between your example and my enqueue() example is that in
foo(), you do modify the parameter - the semantics of foo is that the
parameter is incremented, but in enqueue(), *elem is not modified - no
one expect by purely putting a data to a queue would modify the data.
 
H

hzmonte

Frederick said:
Should "s_fifoItem" contain a "void*", or a "void const*". If the
former, then this implies that the pointer will be used to alter the data
at that address. If the latter, this implies that the data won't be
altered. If you choose to leave it as "void*", then make your function take a "void*".
I meant the latter - the data won't be altered.
 
H

hzmonte

Bill said:
I would go with the former, but a slight modification
of the second solution is to provide an accessor function
which casts away the const from the structure. eg
void *ret;
ret = (void *)(i->elem)
This has essentially exactly the same problems you
describe, but it localizes the cast.
After reading all the related posts in this newsgroup (it turns out
that there are other threads on this subject - just search for "const
poisoning") so far I have felt that this is the most satisfactory
solution.
 
M

Michael Mair

Where did I go wrong? I did make a mistake by saying constifying elem;
what I meant was constifying *elem. That is, the data pointed to by
the pointer is not changed.

You snipped a little bit too much context:
- as I understood you, you wanted to store a copy of a const void *
which itself was not to be const void * but void *; afterwards,
outside the function where you stored the copy, you wanted to modify
the storage through this copy.
- As the compiler correctly did give you a diagnostic, you
complained that this seems wrong.
- It seemed to me that you were not aware of const semantics

Thus my statement.
Make [*]
const void *elem
if you want to never ever modify *elem via i->elem
and never to be able to modify *(i->elem) when you get
the address from i->elem instead of another copy of the
address.
Then "const"ing *elem via "const void *elem" makes sense.

I do want to modify *elem outside this (enqueue) function. That is why
I said I have a dilemma.
Okay.
There is a difference between "the pointer parameter will not be
used in the function to modify what the pointer points to" and
"the address passed via the pointer parameter and its copies made
during the function's lifetime are not used to modify what the
pointer points to". You essentially promise the latter when saying
"const void *elem".

Yes, I want that (latter) promise.
Okay.
Once again: const-ifying elem means "void * const elem" and that

Again, I should have said "const-ifying *elem".
the parameter will not change its value. If you want to be able
to say "the value elem points to is not changed but the value
i->elem points to may be changed" then you have also to pass the
size of *elem and create a copy of *elem:
i->elem = malloc(size);
if (i->elem == NULL) {
/* Your sensible action here */
}
memcpy(i->elem, elem, size);

It would be more precise to say that what I want is "the value elem
points to is not changed within the function but the value i->elem
points to may be changed outside the function".
Your suggestion resolved the problem but requires additional memory
that is not necessary had C provided a way to get around this dilemma.
There is no real need to change *elem in the function and thus no need
to malloc a copy of *elem - the need arises only because C does not
satisfy both goals (type compatibility and the desirability to
represent a parameter as "does not change" in a function) at the same
time.

True; however, this is the only conceptually clean solution
I know. With using "const void *" and 'casting away' const,
you always risk trying to modify a truly const object that
somehow got in there...


Cheers
Michael
 
H

hzmonte

Michael said:
- As the compiler correctly did give you a diagnostic, you complained that this seems wrong.
I am not saying the compiler is wrong. I am just saying that the C
language design seems to be caught in two conflicting goals:
1. Type safety: a "const type *" cannot be assigned to a "type *", for
example. A cast is needed to subvert it.
2. Invariance safety: A parameter that is not modified in a function
can be qualified with 'const' and enforced by the compiler.
Both goals are desirable. And the compiler correctly gives me a
diagnostic when I violate the type safety rule. I am just looking for
a way that I can program it in a way that both goals can be satisfied
at the same time.
True; however, this is the only conceptually clean solution
I know. With using "const void *" and 'casting away' const,
you always risk trying to modify a truly const object that
somehow got in there...
OK, then let me say this: There are at least 3 ways to do it, each has
its benefits and drawbacks:
1. Not using 'const' at all: the drawback is that there is no
"invariance safety" (I coined this term).
2. Casting away the 'const': type safety is subverted.
3. Making a copy of the data: Additional copying and memory is needed.
 
H

hzmonte

To facilitate discussion, a copy of the "Rationale for International
Standard - Programming Languages -C Revision 5.10 April-2003" can be
found
at:http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf :
<quote>
Clause 6.7.3 Type qualifiers:
The C89 Committee added two type qualifiers, const and volatile ...
Individually and in combination they specify the assumptions a compiler
can and must make when accessing an object an lvalue.
The syntax and semantics of const were adapted from C++; the concept
itself has appeared in other langauges. ...
Type qualifiers were introduced in part to provide greater control over
optimization. ...
The basic qualifiers can be characreized by the restrictions they
impose on access and cacheing:
const - No writes through this lvalue. In the absence of this
qualifier, writes may occur through this lvalue.
.... const is specified in such a way that an implementation is at
liberty to put const objects in read-only storage, and is encouraged to
diagnose obvious attempts to modify them, but is not required to trace
down all the subtle ways that such checking can be subverted.
</quote>
1. The document does not explicitly address the use of 'const' to
qualify a parameter of a fucntion. I am not able to find any rationale
for using 'const' in the C++ Standard either. Maybe the justification
is buried in some committee minutes.
2. Though many people cite the desirability of making clear the fact
that the parameter is not changed in the function if it is
const-qualified, from the standpoint of safety and maintainability,
this benefit is not mentioned in the document as a rationale for
including 'const' in the C syntax. And there seems to be different
opinion as to the feasibility/effectiveness of optimizing the code when
a parameter is const-ified.

I am starting to think that maybe const, as used to qualify a function
parameter, cannot be used to satisfy two goals (type safety and
invariance safety) at the same time. Perhaps two different constructs
are needed to satisfy two different goals. For example, a new keyword
'in' - like the one used in Ada to designate an input parameter, is
needed to designate a parameter that is not changed in a function. So
in my enqueue() example, the function would be like this:
void enqueue(t_fifoQueue *q, in void *elem)
{
t_fifoItem *i, *last;

i = malloc(sizeof(t_fifoItem));
i->elem = elem;
last = q->prev;
last->next = i;
i->next = q;
q->prev = i;
}
And 'in' does not play a part in type compatibility; so "i->elem =
elem" is okay.
(And 'in' is not meant to solve the 'const poisoning' problem - that's
a separate issue, involving whether the benefit of doing it is worth
the trouble of cascading its use.)

For those interested, I found many other threads in this newsgroup that
are related to this subject - I never thought this is such a hot topic.
They are with the subjects "Whats the deal with 'const'?" " to const
or not to const " "is const useless and dangerous?" "is const useless
and dangerous?" "ptr to const params to show "read only"" "function
(const char *) vs. function (char *)" "compatible types in assignment"
"problem with constant pointer...". Most, if not all, of these
discussed the usefulness of 'const' (the last discussion thread raises
question of whether casting away the 'const' would induce undefined
behavior) but stopped short of really identifying the conceptual issue
to my satisfaction (not the practical issue of cascading the const).
(There is yet another lengthy discussion thread with the subject "What
Value Does Const Correctness Offer" in a C++ forum at
http://www.cpptalk.net/what-value-does-const-correctness-offer-vt11516.html.)
 
H

hzmonte

Let me refine my arguments. Two means, among others, to ensure safety
are:
1. Immutability on assignment - A variable of type "const float *" is
not assignable to a variable of type "float *", for example.
2. Immutability on parameter passing - A parameter qualified with
'const' is not modifiable inside a function.
Apparently there needs to be two different constructs to achieve these
means. Using the same 'const' to achieve them creates conflicts.
 
M

Michal Nazarewicz

Perhaps two different constructs are needed to satisfy two different
goals. For example, a new keyword 'in' - like the one used in Ada
to designate an input parameter, is needed to designate a parameter
that is not changed in a function. So in my enqueue() example, the
function would be like this:
void enqueue(t_fifoQueue *q, in void *elem) {
t_fifoItem *i, *last;

i = malloc(sizeof(t_fifoItem));
i->elem = elem;
last = q->prev;
last->next = i;
i->next = q;
q->prev = i;
}

Ugh? How would it differ from:

void enqueue(t_fifoQueue *q, void *const elem) {

Secondly, I don't see the use of the keyword "in" or "const" in such
situation. As a programmer who uses given function, I do not care if
function changes the value of argument or not since the changes don't
affect outside of the function.
 
H

hzmonte

Ugh? How would it differ from:
void enqueue(t_fifoQueue *q, void *const elem) {

What I meant by "in void *elem" is that *elem is not changed. "void
*const elem" means elem is not changed, but *elem may be changed.
Secondly, I don't see the use of the keyword "in" or "const" in such
situation. As a programmer who uses given function, I do not care if
function changes the value of argument or not since the changes don't
affect outside of the function.

Well, that is a hot topic for debate. As I said, there are tons of
discussions on the subject of the usefulness of const. Here, I am
assuming it is useful to designate an input parameter as such. Of
course if this assumption does not hold, it is moot to discuss whether
'in' is better than 'const'.
 
M

Michal Nazarewicz

What I meant by "in void *elem" is that *elem is not changed. "void
*const elem" means elem is not changed, but *elem may be changed.

Yes, exactly. That's because *elem will be changed by another
function which takes t_fifoQueue as an argument. You can't claim that
*elem won't be changed and then change it in another function.
Well, that is a hot topic for debate. As I said, there are tons of
discussions on the subject of the usefulness of const. Here, I am
assuming it is useful to designate an input parameter as such. Of
course if this assumption does not hold, it is moot to discuss whether
'in' is better than 'const'.

Adding new keyword which doesn't provide anything new is definitly not
a good idea.
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top