[comp.std.c++] 'const' revisited

twagner@madrone.berkeley.edu (Tim Wagner) (08/10/90)

The recent discussion on this newsgroup about the conflicting uses of
'const' gives one pause.  Shouldn't something as useful as the two
notions here be corrected in C++?

The key distinction is between a restriction of access through a particular
binding, and a restriction on access to an underlying object.  In the
first case, the programmer merely wants to tighten the type of a name (binding)
to get additional compile-time error-checking for what he/she has decided is
an invalid access attempt.  In the second case, the object being referred to
is truly a constant; the compiler is justified in placing it in read-only
storage, and preventing (to the extent possible) any non-read-only access
attempt through any binding at all.

For a name access restriction, casting into the equivalent non-restricted type
makes sense.
For object access restriction, casting into the non-restricted type should
produce a stern warning, since writing to such an object can produce runtime
errors depending on the implementation.

One problem that currently exists is the confusion between objects and
bindings.  Consider the following code:

int i;
const int& r = i;
...
i = 5; /* Legal */
r = 6; /* Compiler warning about assigning to non-const object */

In the second line, the 'const' essentially refers to the binding of
the name 'r', NOT to the object 'r' refers to (which is obviously non-const).

In the following code, the 'const' refers to the object AND the binding:

const int i;
int& r = i;
...
i = 5; /* Compiler warning about assigning to non-const object */
r = 6; /* No warning, and probably a runtime error */

The confusion over the meaning of 'const' is troublesome, and creates
an asymmetry in whether it applies to the object or the binding.  In
the latter case, one might very well desire a read-write object with
a read-only binding through i (but not through r).  This is not expressible
in the language per se.

One possible solution is to apply machine-independent semantics for the
keyword volatile.  That is, the following chart will obtain:

      | volatile | none
-------------------------
const |     A    |  B
-------------------------
none  |     C    |  D
-------------------------

A:  A name declared with these specifiers has a read-only access restriction
    associated with it (i.e., the name is read-only), but the underlying
    object is read/write.  Casts to B can occur implicitly; casts to
    C or D must be explicit.

B:  A name declared with only the 'const' specifier has both the read-only
    access restriction, AND the object it refers to is declared to be
read-only.
    The compiler is justified in replacing the value (where possible) with
    its value (i.e., inlining the value), caching it in a register, etc.
    Casts to A occur implicitly; casts to C or D must be explicit.

C:  This suggests to the compiler that the object cannot be held in read-only
    storage, even if it "believes" otherwise.  There may be additional
optimizing
    actions that are prevented by the addition of the "volatile" specifier.
    Casts to D are implicit; casts to A and B must be explicit.

D:  Normal type.  Casts to A,B,and C are implicit. The compiler is free to
    treat the object as read-only or read-write, depending on what sort of
    analysis it is capable of.  The binding, in any case, is read-write.

This has two advantages: it supplies a useful semantic meaning to 'volatile',
and it distinguishes between two important, but distinct, meanings of
read-only.  gcc, for instance, will (in my mind correctly) handle a
global declaration of "const volatile int i;" by creating a read-write object
(i.e., not in the text segment) with read-only access restrictions for
the name 'i'.  THIS SHOULD BE CODIFIED INTO LAW!  Furthermore, a code fragment
such as:

int i;
const int *p = &i;

should be flagged as a violation: the semantics are inconsistent here, as
the object 'p' points to is NOT const.  This should really be written

int i;
const volatile int *p = &i;

since the BINDING is read-only, rather than the object being pointed to.


The use of 'const' already confuses the issue of binding and object; the
suggested style requires 'const' to refer to the object and the binding, unless
'volatile' modifies it to refer to the binding only.  Since this appears to
be the only place where bindings and objects are treated separately already,
the change in semantics appears justified.  Furthermore, the use of
'volatile' to imply that the object is read/write is consistent with what
little semantics are described for it in E&S.  If a particular site or
implementation requires additional semantics formerly produced by 'volatile',
a new keyword or pragma may be used instead.

*******************
Tim A. Wagner
Graduate Researcher, UCB
My opinions are my own, etc.
twagner@sequoia.Berkeley.EDU
*******************

sakkinen@tukki.jyu.fi (Markku Sakkinen) (08/10/90)

In article <26909@pasteur.Berkeley.EDU> twagner@madrone.berkeley.edu (Tim Wagner) writes:
>
>The recent discussion on this newsgroup about the conflicting uses of
>'const' gives one pause.  Shouldn't something as useful as the two
>notions here be corrected in C++?
> [rest of long article deleted]

Sorry, I have not seen the previous discussion.
In my ECOOP'88 paper I had a much simpler suggestion that would
be sufficient for most practical cases.

Rename "pointer to constant" as it currently stands in C++
into something like "nonmodifying pointer" and introduce
also true pointers to constant. If we call ordinary pointers
"modifying pointers", then obviously the only safe conversions are
from "pointer to constant" to "nonmodifying pointer" and
from "modifying pointer" to "nonmodifying pointer".

References can be handled analogously to pointers, of course.

Markku Sakkinen
Department of Computer Science
University of Jyvaskyla (a's with umlauts)
Seminaarinkatu 15
SF-40100 Jyvaskyla (umlauts again)
Finland
          SAKKINEN@FINJYU.bitnet (alternative network address)

mckenney@sparkyfs.istc.sri.com (Paul Mckenney) (08/10/90)

In article <26909@pasteur.Berkeley.EDU> twagner@madrone.berkeley.edu (Tim Wagner) writes:
> [....]
>One possible solution is to apply machine-independent semantics for the
>keyword volatile.  That is, the following chart will obtain:
>      | volatile | none
>-------------------------
>const |     A    |  B
>-------------------------
>none  |     C    |  D
>-------------------------
>A:  A name declared with these specifiers has a read-only access restriction
>    associated with it (i.e., the name is read-only), but the underlying
>    object is read/write.  Casts to B can occur implicitly; casts to
>    C or D must be explicit.

I disagree with this interpretation; the name declared might well refer
to a register that is updated by hardware but that is not to be touched by
at least part of the software.  For example, the register might be
part of a real-time clock, and writing to the register might have
the effect of setting the clock -- not an operation to be performed
lightly!

In this case, the compiler should prohibit modifying the object referred
to by the name, but must not assume that a copy of the object still has
the same value as the object itself (since the object is subject to change
by outside agents).

Therefore, I feel that casts from A to all of B, C, and D should be explicit.


					Thanx, Paul

jimad@microsoft.UUCP (Jim ADCOCK) (08/14/90)

I think the idea of using 'volatile' with 'const' to help resolve some
of const's multiple meanings is a good idea -- but I'm not sure the 
other suggestions follow.  One problem I see is that there are [at least]
two reasonable interpretations that one can put on 'volative' + 'const'

1) A read-only access permission to something that can be changing.  A
read-only access permission to a clock register, for example.  One might
not want to grant read/write permission to such a register because then
anyone can change the time.

2) A statement that the internals of an object might change, but from the
public interface to the object, the object looks unchanged.  An example 
would be a routine that modifies cached values in an object, such that those
values can be accessed faster in the future, but otherwise the state of
the object appears unchanged from the outside world.  This is the traditional
case where people want to cast away from const in a routine whose parameters
are declared const.

One possibility is to overload the meaning of 'const' + 'volatile' depending
on the order of the two:

const volatile vs
volatile const

I think this word ordering is enough hair splitting to cause people a 
lot of trouble remembering, but yet I am loath to introduce yet-another key
word.  So I have mixed feelings about this.  Restating, and expanding the
original matrix, I get:

A) volatile const.  An object that appears const to the outside world that
may be changed internally.  An example might be an object that caches certain
values for faster access in the future.

B) const.  A strictly read-only object that doesn't change.

C) volatile.  A read/write object that may change 'of its own accord.'  
This could include changes made via non-portable coding hacks.
A compiler shouldn't apply optimizations that assume the object is going 
to stay the same.

D) none.  A read/write object that the compiler can apply reasonable optimi-
zations to.  The object doesn't change except via legitimate accesses
within the language.  A compiler can assume that non-portable coding hacks
are not being used.

E) const volatile.  Read-only permissions to an object that may be changing
of its own accord.  A example might be a clock register to which write 
permissions need to be denied, because people shouldn't be allowed to change
the time.  Compilers can't optimize.

"Safe" conversions that could be applied implicitly are:

D to B, C, E, A
B to E
C to A, E
A to C, E
E to none-of-the-above

gregk@cbnewsm.att.com (gregory.p.kochanski) (08/14/90)

AARGH!
One might as well use const(1) const(2) const(3) ... rather than
trying to express these things in terms of non-intuitive ordering
of const and volatile.  Unless the syntax is memorable, it will not be
used or used correctly.   There is such a thing as being excessively
parsimonius with keywords.

One can take this discussion further.  Imagine an object that contains
a pointer to some data (e.g. an array class):

class array	{
	double *data;
	...
	};
Now, we can distinguish (if we really want to) even more cases:
1) the array object is constant, and so is the data
2) the array object is constant, but the data could be changed
3)....
n+1) this function promises not to change the data, but it might
	change the array object
n+2) this function promises not to change the array object or the data
m+1) this function promises not to even think about changing anything
	which might, in the future, be used to calculate future values which
	might be placed in the data of this array object.

One can make this an arbitrarily complex situation by considering an
item which can reside on a linked list:
class ll	{
	ll *next;
	...
	};
Now, you can promise not to modify
this object, or the one it points to, or the next one,
but I might just modify the one after that.

To encode things like that is silly:
const volatile const const volatile const const const volatile ll(2);

davidm@uunet.UU.NET (David S. Masterson) (08/15/90)

In article <56514@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes:

   A) volatile const.  An object that appears const to the outside world that
   may be changed internally.  An example might be an object that caches
   certain values for faster access in the future.

   E) const volatile.  Read-only permissions to an object that may be changing
   of its own accord.  A example might be a clock register to which write
   permissions need to be denied, because people shouldn't be allowed to
   change the time.  Compilers can't optimize.

Might this not be more easily accomplished by public/private interfaces?  In
the private interface of the object, the value is defined as volatile (or
'none').  However, the public interface would have a function that returns a
'const pointer*' to the private member data.

Also, 'volatile' and 'const' seem to differ on the key point of compiler
optimizations (the first can't be, but the second one can be).  Doesn't the
(A) and (E) definitions hurt the 'const'ness of the object from the
compilation standpoint, so making the previous paragraph a more 'desirable
mode of operation?

I am one that believes 'const' should be constant and enforced as such by the
compiler.  Casting is not a valid trick for getting around this.
--
====================================================================
David Masterson					Consilium, Inc.
uunet!cimshop!davidm				Mtn. View, CA  94043
====================================================================
"If someone thinks they know what I said, then I didn't say it!"

nelson@melodian.cs.uiuc.edu (Taed Nelson) (08/15/90)

In article <56514@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes:
> 
>    A) volatile const.  An object that appears const to the outside world that
>    may be changed internally.  An example might be an object that caches
>    certain values for faster access in the future.
> 
>    E) const volatile.  Read-only permissions to an object that may be
changing
>    of its own accord.  A example might be a clock register to which write
>    permissions need to be denied, because people shouldn't be allowed to
>    change the time.  Compilers can't optimize.


Our project has often felt the need for a "mutable" object class, which would
  be essentially equivalent to the "volatile const" above.  I also see the
  great need for the second.

Personally, I do not think that a const --> non-const class should be allowed
  (or at least highly discouraged) for the same reason that you cannot cast
  from a register to a static storage.  But that's all been discussed.

The example where we really felt the need for something between non-const
  and const (and that being constant to the user but the object may change
  its internal state) is for objects which have a "current placeholder" type
  of field.

For example, we have a Dictionary class which is kept current by the system,
  not the user / user program.  Whenever the user wants to use the dictionary,
  we give them a constant reference to it.  This allows them to extract entries
  (which return constant references as well) since that is a const
member function.
  Sometimes, though, the user will want to iterate over the entire dictionary.
  For that reason, we provide them with various First() and Next() functions.
  Unfortunately, the "current entry" which is needed for the Next() needs to
  be changed, thus violating the "constness" of the object.

We solved this problem by providing an "iterator" class which the user uses to
  provide the Next() functions (the iterator does the storing of the state
  information), but I don't think it very clean.  Especially since it requires
  another class...

jimad@microsoft.UUCP (Jim ADCOCK) (08/16/90)

In article <1990Aug14.125734.28015@cbnewsm.att.com> gregk@cbnewsm.att.com (gregory.p.kochanski) writes:
>AARGH!
>One might as well use const(1) const(2) const(3) ... rather than
>trying to express these things in terms of non-intuitive ordering
>of const and volatile.  Unless the syntax is memorable, it will not be
>used or used correctly.   There is such a thing as being excessively
>parsimonius with keywords.

Yes.  You highlight well the concerns I had in proposing the ordering
approach to overloading.

What you don't do it make a counterproposal as to how to resolve the 
present problems with const.  Any suggestions?  Four different approaches
might be:

1) Const means const.  Period.  No exceptions.

2) Volatile const and/or const volatile mean the same thing:  A read-only
object that can change of its accord.  Const by itself means const without
exception.  Volatile const is then an object that cannot be
enregistered in whole nor part.

3) Volatile const verses const volatile distinction.

4) Leave things the way they are now -- a muddle where people cast away from
const, leading either to compilers that can't optimize on const, or compilers
that do optimize on const, with "quasi-const" routines that break the
caller's code.

shopiro@alice.UUCP (Jonathan Shopiro) (08/16/90)

The meaning of const has been discussed quite a bit around here.  I
used the term "bit-wise const" to mean that the storage may not be
changed, and "meaning-wise const" otherwise.  It is clear that both
interpretations of const are meaningful and useful, and that neither
subsumes the other.  The higher-level abstractions that C++ was
particularly designed to support require the meaning-wise
interpretation of const, but effective use of read-only memory (which
has traditionally been an important issue at AT&T) requires the
bit-wise interpretation.

Our compromise partitions the set of types.  Meaning-wise const types
are those with constructors or destructors or arrays of these.  (Note
that a type with a member or base that has a constructor or destructor
automatically has one).  The rest are bit-wise const.  An implementation
may not put a const instance of a meaning-wise const type into
read-only memory, and a programmer may cast const aside from a pointer
to a meaning-wise type and be assured that the program will work as if
the pointer and its referent had never been declared const in the
first place.  E&S says only that const instances of bit-wise const
types may be put into read-only memory, but I think it would be
reasonable for X3J16 to extend this interpretation so that an
implementation would be allowed to assume that a const pointer to a
bitwise const type points to memory that will not change.  (Const
volatile would tell the implementation not to make this assumption).

On a separate topic, I have heard there is some confusion about
Sh[ao]piro.  Jonathan Shapiro (shap@sgi.com) is a friend of mine and a
C++ expert (so we usually agree), but he is not me.
-- 
		Jonathan Shopiro
		AT&T Bell Laboratories, Warren, NJ  07060-0908
		research!shopiro   (201) 580-4229

DEREK@AppleLink.apple.com (Derek White) (08/16/90)

In article <1990Aug14.224806.21375@brutus.cs.uiuc.edu> 
nelson@melodian.cs.uiuc.edu (Taed Nelson) writes:
> The example where we really felt the need for something between non-const
> and const (and that being constant to the user but the object may change
> its internal state) is for objects which have a "current placeholder" type
> of field.
> 
> For example, we have a Dictionary class which is kept current by the system,
> not the user / user program.  Whenever the user wants to use the dictionary,
> we give them a constant reference to it. This allows them to extract entries
> (which return constant references as well) since that is a const
> member function.
> Sometimes, though, the user will want to iterate over the entire dictionary.
> For that reason, we provide them with various First() and Next() functions.
> Unfortunately, the "current entry" which is needed for the Next() needs to
> be changed, thus violating the "constness" of the object.
> 
> We solved this problem by providing an "iterator" class which the user 
> uses to provide the Next() functions (the iterator does the storing of the 
> state information), but I don't think it very clean.  Especially since it 
> requires another class...

I think the iterator solution is very clean.  Seperate iterator classes 
allow multiple iterations over the same collection.  They also neatly 
divide the job of iterating, and as you have shown, they pull out the 
variable part of a constant object.

In fact, the recent examples that have shown a need for "partially const" 
objects have fallen in the same catagory:  I think they can be solved by 
seperating the constant and variable portions of an object into constant 
and varying fields or classes.

Although it may be expedient to make a statement that an object is const 
when it really isn't, I think in the long run honesty is the best policy.  
I suggest a hard line approach: const is constant.


Derek White                          AppleLink: DEREK
              - Standard Discliamer -

bright@Data-IO.COM (Walter Bright) (08/16/90)

In article <56586@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes:
<4) Leave things the way they are now -- a muddle where people cast away from
<const, leading either to compilers that can't optimize on const, or compilers
<that do optimize on const, with "quasi-const" routines that break the
<caller's code.

This is the best solution. There are cases where the user needs to cast
away from const, even though he never changes the contents (such as in
interfacing to an existing library which doesn't use pointers to const).

As a case in point, let's try to implement an ANSI C memchr:

	void *memchr(const void *p,int c,size_t n)
	{	while (n--)
		{	if (*(const char *)p == c)
				return (void *) p;	/* !!! */
			p++;
		}
		return NULL;
	}

(Note that, in ANSI C, you can use functions like memchr() to get rid
of the 'constness' of a pointer even if you disallowed the cast!
Therefore, disallowing the cast would require 2 versions of memchr(), one
for const void* and the other for void*, yeech. The ripples of this
problem go on and on...)

Casting a void* to a const void* requires an explicit cast, and so the user
is presumed to know what he's doing (just like in the rest of C).

The optimizer can presume that the contents of non-volatile const
variables do not change. If a user, by playing tricks, changes the variable,
he loses. This is similar to the case:

	int a,b,*p;

	b = 4;
	p = &a;
	c = b * 3;
	p[1] = 5;		/* changes b! */
	d = b * 3;		/* 12 is stored in d by an optimizing compiler,
				   15 by a non-optimizing one, though both
				   compilers are ANSI conforming.	*/

shopiro@alice.UUCP (Jonathan Shopiro) (08/16/90)

In article <11194@alice.UUCP>, I wrote:
> 
> 
> ...  E&S says only that const instances of bit-wise const
> types may be put into read-only memory, but I think it would be
> reasonable for X3J16 to extend this interpretation so that an
> implementation would be allowed to assume that a const pointer to a
> bitwise const type points to memory that will not change.
> 
Oops!!  Consider the following perfectly reasonable function:

	void
	increase(int* ip, const int* cip)
	{
		*ip += *cip;
	}

and a call to the function:

	main()
	{
		int		i = 3;
		int*		p = &i;
		const int*	cp = &i;
		increase(p, cp);
		cout << *cp << "\n";
	}

This function had better print "6", but if my unthinking proposal
were accepted, it would be permissible for it to print "3."  Of
course, in this case, the aliasing is obvious, but in general
aliasing is impossible to detect at compile time.

I would be willing to consider allowing the implementation to assume
that memory pointed to by a const pointer to a bitwise const type
points to memory that will not change, but only if the implementation
can prove that the memory cannot be changed through any legally
constructed alias.  (For example the pointer points to an automatic
variable in that function and the address of that variable has not
been saved elsewhere).

On a separate topic, I have changed my .signature but I am still the
same person.
-- 
		Jonathan E. Shopiro
		AT&T Bell Laboratories, Warren, NJ  07059-0908
		shopiro@research.att.com   (201) 580-4229

gregk@cbnewsm.att.com (gregory.p.kochanski) (08/17/90)

In article <56586@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes:
>What you don't do it make a counterproposal as to how to resolve the 
>present problems with const.  Any suggestions?  Four different approaches
>might be:
>
>1) Const means const.  Period.  No exceptions.
>
2) Volatile const, const volatile, and so forth
>3) Volatile const verses const volatile distinction.
>4) Leave things the way they are now -- a muddle where people cast away from

My current C++ practice (also ANSI C) is to take option 1.
I think the best way to handle this -- in an ideal world-- is to expand
the concept of interface files so that they can report detailed information
about the functions that they describe.  This might allow objects to
be partially const (use for cacheing information), and might allow
deeper optimization (in cases where a const object contains a pointer
where the pointed-to data might or might not be affected by the function).
The question is "how?"
Well, an off-the-cuff idea (no more than 10 min of thought) is the
following:
Extend the concept of function prototypes to also contain sortof pseudocode.
The pseudocode would bethe minimum C++ necessary to describe what
parts of an object change (or other interesting properties).
Take a function   int f(someclass& x),
where 
class someclass	{
	int sum_of_all_ps;
	public:
	int *p;
	int q;
	int sum() C {return sum_of_all_ps ? sum_of_all_ps : add_em_up(p);}
	};
Here, sum_of_all_ps is a cache which is presumably hard to calculate.
So, if f is the following:
int f(someclass& x)
{
 x.q += x.sum();
 return x.sum();
}

We might have the following pseudocode:
prototype int f(someclass& x) { x.q=0; }
which would mean that f() can change x.q, but nothing else in x.
Note that the need for 'const' as a keyword in function calls
goes away entirely.

Any comments?

Greg Kochanski gpk@physics.att.com
AT&T Physics Research -- Physics for fun and profit.

pcg@cs.aber.ac.uk (Piercarlo Grandi) (08/17/90)

On 15 Aug 90 21:28:31 GMT, DEREK@AppleLink.apple.com (Derek White) said:

DEREK> In fact, the recent examples that have shown a need for
DEREK> "partially const" objects have fallen in the same catagory: I
DEREK> think they can be solved by seperating the constant and variable
DEREK> portions of an object into constant and varying fields or
DEREK> classes.

Excellent point. Moreover 'const' in C++ already has the right
definition; thank Stroustrup for not having botched it like in Ansi C. i
would hate that any debate on 'const' opened the way for the usual call
to greater Ansi C conformity. Fortunately C++ is quite a different
language.

DEREK> Although it may be expedient to make a statement that an object
DEREK> is const when it really isn't, I think in the long run honesty is
DEREK> the best policy.  I suggest a hard line approach: const is
DEREK> constant.

Again, we go back to my usual and tired observation; what we wnat to
achieve is reuse of interface, proof and implementation, and overloading
keywords can only go so far to this end (see how strained is the syntax
for the = 0 virtual function case). C++ should provide mechanisms
towards this, and mostly implementation oriented. 'const' as such is a
statement on the implementation of an object, not on its interface or
semantics.

--
Piercarlo "Peter" Grandi           | ARPA: pcg%uk.ac.aber.cs@nsfnet-relay.ac.uk
Dept of CS, UCW Aberystwyth        | UUCP: ...!mcsun!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk

chased@rbbb.Eng.Sun.COM (David Chase) (08/18/90)

Speaking not as a C++ expert (because I'm not) but as someone who
might care about what an optimizing compiler could infer from "const",
I think that #pragma's should be used to give the compiler hints to
enable optimization, and that "const" (and its ilk) should be used in
whatever sense is most helpful to programmers and program maintainers.

The reasoning goes like this -- (1) there's LOTS of things that a
compiler might want to know about the behavior of a subroutine, (2)
and it isn't clear that "const" is the most important of them, (3)
adding keywords or abusing combinations of existing keywords to get
the additional information is likely to result in confusing cruft
irrelevant to the correctness of the program (much like "register" in
the old days).  The choice of what is best to know about a program
could well vary from processor to processor or from compiler to
compiler, so it isn't clear that this should be standardized just yet.
Thus, don't make it part of a language that is being standardized.

For example, a procedure passed a pointer might modify it or not,
or might store it in a global variable for later modification or
examination.  Pass the address of a local to a procedure that stores
it in a global, and you can't eliminate tail-calls.  Even if a
procedure does assign through a pointer, it's good to know when it
doesn't store the pointer someplace where OTHER procedures can assign
through it.

It's also slightly useful, on a multiprocessor, to know if a
subroutine called from within a loop modifies global data.  It's much
easier to run the loop body in parallel if any subroutines called
within the loop don't modify globally visible (shared) data.

David Chase
Sun Microsystems

davidm@uunet.UU.NET (David S. Masterson) (08/20/90)

In article <140877@sun.Eng.Sun.COM> chased@rbbb.Eng.Sun.COM (David Chase)
writes:

   Speaking not as a C++ expert (because I'm not) but as someone who
   might care about what an optimizing compiler could infer from "const",
   I think that #pragma's should be used to give the compiler hints to
   enable optimization, and that "const" (and its ilk) should be used in
   whatever sense is most helpful to programmers and program maintainers.

I agree with this sentiment, but think the thrust should be reversed (in
general).  That is, I think #pragma's should be used to give the compiler
hints that standard optimization will not work with the current program.  The
language itself (since it derives from C) should promote optimization in its
structure, not as a by-product of implementation specifics (as in the
#pragma's).
--
====================================================================
David Masterson					Consilium, Inc.
uunet!cimshop!davidm				Mtn. View, CA  94043
====================================================================
"If someone thinks they know what I said, then I didn't say it!"

davidm@uunet.UU.NET (David S. Masterson) (08/20/90)

In article <PCG.90Aug17170737@athene.cs.aber.ac.uk> pcg@cs.aber.ac.uk 
(Piercarlo Grandi) writes:

   Again, we go back to my usual and tired observation; what we wnat to
   achieve is reuse of interface, proof and implementation, and overloading
   keywords can only go so far to this end (see how strained is the syntax
   for the = 0 virtual function case). C++ should provide mechanisms
   towards this, and mostly implementation oriented. 'const' as such is a
   statement on the implementation of an object, not on its interface or
   semantics.

I agree that overloading keywords "can only go so far", but the interface of
an object is *part* of the implementation of the object.  I see no reason why
'const' is not also a statement of the interface to an object (as in constant,
public pointer to private data areas).  Thus far, I have not seen anything
that needs a partial const that couldn't be solved by using a constant, public
interface to a private data item.
--
====================================================================
David Masterson					Consilium, Inc.
uunet!cimshop!davidm				Mtn. View, CA  94043
====================================================================
"If someone thinks they know what I said, then I didn't say it!"

davidm@uunet.UU.NET (David S. Masterson) (08/20/90)

In article <2648@dataio.Data-IO.COM> bright@Data-IO.COM (Walter Bright) writes:

   As a case in point, let's try to implement an ANSI C memchr:

	   void *memchr(const void *p,int c,size_t n)
	   {	while (n--)
		   {	if (*(const char *)p == c)
				   return (void *) p;	/* !!! */
			   p++;
		   }
		   return NULL;
	   }

Hmmm.  I take it back, this does look like a problem with a constant 'const'.
I'll have to think about this.
--
====================================================================
David Masterson					Consilium, Inc.
uunet!cimshop!davidm				Mtn. View, CA  94043
====================================================================
"If someone thinks they know what I said, then I didn't say it!"

jimad@microsoft.UUCP (Jim ADCOCK) (08/20/90)

In article <140877@sun.Eng.Sun.COM| chased@rbbb.Eng.Sun.COM (David Chase) writes:
|Speaking not as a C++ expert (because I'm not) but as someone who
|might care about what an optimizing compiler could infer from "const",
|I think that #pragma's should be used to give the compiler hints to
|enable optimization, and that "const" (and its ilk) should be used in
|whatever sense is most helpful to programmers and program maintainers.
|
|The reasoning goes like this -- (1) there's LOTS of things that a
|compiler might want to know about the behavior of a subroutine, (2)
|and it isn't clear that "const" is the most important of them, (3)
|adding keywords or abusing combinations of existing keywords to get
|the additional information is likely to result in confusing cruft
|irrelevant to the correctness of the program (much like "register" in
|the old days).  The choice of what is best to know about a program
|could well vary from processor to processor or from compiler to
|compiler, so it isn't clear that this should be standardized just yet.
|Thus, don't make it part of a language that is being standardized.
....

This is fine -- if C++ [as a whole] is willing to move away from the 
traditional "un*x" model of separate compilation, linking .h, .c files
etc.  -- Its just that lots of compromises have been made to the language
already to make it fix into this traditional C-like model of compilation
and linking.

The problem I see it, if you give the compiler all this extra #pragma 
information, where does the compiler store the pragma information such
that separately compiled modules can access it?  -- I guess the #pragmas
could go into the .h files, further weakening the distinction between
interface and implementation.  [And leading to even longer .h compilation
times] Otherwise, one has to add new fields in the
.o files, or add an additional database of information kept on a 
project-wide basis....

Optimizations based on const follows in a straight forward manner from const'
ness -- if compilers can rely on const on a contractual basis, and require no 
information from a programmer beyond what programmer's currently are
providing.  Optimizations based on const also encourage good programming
practice of declaring const on const things.

....Alternatively, maybe its just time to admit that the C-model of separate
compilation/linking is done for, and toss out the C++ language hacks based
on those restrictions.  But please, lets not get into a worse of both 
worlds situation where the language hacks remain, but other decisions are
made forcing C++ compilers towards auxiliary databases of information.

jimad@microsoft.UUCP (Jim ADCOCK) (08/21/90)

In article <2648@dataio.Data-IO.COM> bright@Data-IO.COM (Walter Bright) writes:
|In article <56586@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes:
|<4) Leave things the way they are now -- a muddle where people cast away from
|<const, leading either to compilers that can't optimize on const, or compilers
|<that do optimize on const, with "quasi-const" routines that break the
|<caller's code.
|
|This is the best solution. There are cases where the user needs to cast
|away from const, even though he never changes the contents (such as in
|interfacing to an existing library which doesn't use pointers to const).
|

[memchr example]

|(Note that, in ANSI C, you can use functions like memchr() to get rid
|of the 'constness' of a pointer even if you disallowed the cast!
|Therefore, disallowing the cast would require 2 versions of memchr(), one
|for const void* and the other for void*, yeech. The ripples of this
|problem go on and on...)

Why?  Why is this not exactly the solution people should be using --
Make a const and a non-const version of a routine, and thus avoid the
const-hacking?  Why allow the memchr code you suggest when, at best,
its non-portable?

Note:  In an optimizing compiler its easy to imagine 
situations where the memchr example is going to bust caller code --
even if memchr *doesn't* change the value of the thing cast from const.
Again, the problem is that calling a routine that violates its const'ness
contract changes the optimization that can be performed in the caller's
environment -- but in the face of separate compilation there's no way
for the compiler to know that!

|Casting a void* to a const void* requires an explicit cast, and so the user
|is presumed to know what he's doing (just like in the rest of C).

Even though what s/he's doing is potentially busting someone else's code
that uses this separately compiled module?  The problem is it's the
calling sequence that gets busted when const'ness is violated -- not
the called code!

|The optimizer can presume that the contents of non-volatile const
|variables do not change. If a user, by playing tricks, changes the variable,
|he loses. This is similar to the case:
|
|	int a,b,*p;
|
|	b = 4;
|	p = &a;
|	c = b * 3;
|	p[1] = 5;		/* changes b! */
|	d = b * 3;		/* 12 is stored in d by an optimizing compiler,
|				   15 by a non-optimizing one, though both
|				   compilers are ANSI conforming.	*/


I don't think the examples are very similar.  In the case given here,
a programmer busts his/her own code.  In the cast from const case, a 
programmer [potentially] busts someone else's code.  And it might
be a long time for that bug to show up in a definative manner.  Chances are,
programmer's would instead blame the compiler.  And then compiler writers
will pessimize, rather than optimize, on const.  This to me is the bottom
line.  If compiler's cannot trust a function's const-ness on a contractual
basis, compiler writer's will not create compilers that optimize on a 
function's const'ness.

So again, in the particular case where a cast from const violates a 
functions implied contract, I think it deserves an error message,
not a warning.

jimad@microsoft.UUCP (Jim ADCOCK) (08/21/90)

In article <11194@alice.UUCP> shopiro@alice.UUCP (Jonathan Shopiro) writes:
|
|Our compromise partitions the set of types.  Meaning-wise const types
|are those with constructors or destructors or arrays of these.  (Note
|that a type with a member or base that has a constructor or destructor
|automatically has one).  The rest are bit-wise const.  An implementation
|may not put a const instance of a meaning-wise const type into
|read-only memory, and a programmer may cast const aside from a pointer
|to a meaning-wise type and be assured that the program will work as if
|the pointer and its referent had never been declared const in the
|first place.  E&S says only that const instances of bit-wise const
|types may be put into read-only memory, but I think it would be
|reasonable for X3J16 to extend this interpretation so that an
|implementation would be allowed to assume that a const pointer to a
|bitwise const type points to memory that will not change.  (Const
|volatile would tell the implementation not to make this assumption).

Such an interpretation prevents the enregistering of members across a 
"const" member function.  This puts C++ at a competitive disadvantage
to other OOPLs that maintain function calls on a contractual basis.
Eventually C++ would become know as "that slow OOPL", not "that fast OOPL."
I think this would be a mistake.  Const should mean const.  Casts from
const should be at best implementation defined when they violate a
function's implied contract.

jimad@microsoft.UUCP (Jim ADCOCK) (08/21/90)

In article <CIMSHOP!DAVIDM.90Aug20002153@uunet.UU.NET> cimshop!davidm@uunet.UU.NET (David S. Masterson) writes:
>
>I agree with this sentiment, but think the thrust should be reversed (in
>general).  That is, I think #pragma's should be used to give the compiler
>hints that standard optimization will not work with the current program.  The
>language itself (since it derives from C) should promote optimization in its
>structure, not as a by-product of implementation specifics (as in the
>#pragma's).

I think this is a pretty good solution.  Its only disadvantage is that the
#pramga's need to be in the .h files, further eroding the distinct 
between specification and implementation.  But, if you don't do it,
you don't see the ugliness in your .h files....

This ties in well with the C++ philosophy that people who don't need a 
feature shouldn't be the ones to pay for it -- people who don't cast
away from const shouldn't pay a price for those who do cast-away consts.
I like this a lot better than Koenig's suggestion where everybody has
to pay the price for cast-aways!

twagner@baobab.berkeley.edu (Tim Wagner) (08/27/90)

Unfortunately, what does one do when per-instance information is
required?  The following cannot possibly be acceptable to anyone:

#pragma this-means-really-const
const i;
#pragma this-means-interface-const
const j;
...

'pragma' as a replacement for language-integrated type information
would be the supreme sacrifice in this case.