Topic: Responses to ~const 1.6: Experience?


Author: jbuck@galileo.berkeley.edu (Joe Buck)
Date: 20 Feb 91 02:15:07 GMT
Raw View
I was with Tom on his ~const proposal until he proposed the following
bogus application:

In article <NGO.91Feb19114531@tammy.harvard.edu>, ngo@tammy.harvard.edu (Tom Ngo) writes:
|>          * Iterator.  An iterator might be a pair of methods init()
|>            and next() designed to be used like this:
|>
|>                const A obj;   // container class of some kind
|>                obj.init(); while( obj.next() ) { ... }
|>
|>            Here, I would either make the methods init() and next()
|>            ~const, or I would make the data members that record the
|>            state of the iterator ~const.

This is wrong, wrong, wrong.  next() really has changed obj's internal
state, because it has changed the effect of calling next() on that
object.  That is, the state of the internal pointer really is visible
outside the class.  I would recommend against using this to justify
~const.  I would also recommend against this style of iterator.

I prefer having a separate iterator class in such cases, and replacing
the init() and next() actions with something like

 const Container obj;  // container class

 ContainerIter next(obj); // next is an iterator for Containers
 InsideObject* p;
 while ((p = next++) != 0) p->doSomething();

I don't care as much about the syntax, but I recently went through
a large system and got rid of iterators like Tom describes and replaced
them with iterators like I describe.  Why?

Tom's type of iterator means there are large numbers of objects floating
around the system with magic internal pointers.  Users forget to do the
.init() call or don't do them consistently, so the system has bugs in
it that depend on the order of iterator calls by who knows who.  Also,
there are thousands of these wasteful pointers floating around.

If ~const is implemented, it should only be used to support things that
don't affect the externally visible behavior of the object, like cacheing.
Tom's other three applications are all essentially cacheing of some sort:
there's an internal buffer that speeds up the operation of the class, and
equivalent, slower all-const form could have been written.


--
Joe Buck
jbuck@galileo.berkeley.edu  {uunet,ucbvax}!galileo.berkeley.edu!jbuck




Author: ngo@tammy.harvard.edu (Tom Ngo)
Date: 19 Feb 91 16:45:31 GMT
Raw View
Background information to this posting was in a very recent summary.

A proposed extension to C++ is much more credible if one can cite past
experience with it.  Obviously nobody has yet put ~const into a
compiler and tried it out.  However, many people have had the need to
use the equivalent of both ~const data members and member functions.
It is experience of this sort that I would like to solicit.

Under what circumstances have you had to cast away constness?  What
problems have you run into?  Do you abide by some coding convention?
And a specific question: would it really be necessary to allow ~const
member functions, or are all needs covered by ~const data?  Let me
kick off the discussion by describing a few categories of
applications, then giving an example of the convention that I have
used as a workaround to not having ~const.

(1) Application:

    I have needed to cast away constness when I have wanted to change
    an object's representation without changing its meaning.
    Examples:

         * Self-resizing array.  When I got past the bounds of the
           existing array I would call a private method grow(), which
           would allocate new memory and copy contents as necessary.
           Calls for a ~const member function; see item (2) below.

         * Iterator.  An iterator might be a pair of methods init()
           and next() designed to be used like this:

               const A obj;   // container class of some kind
               obj.init(); while( obj.next() ) { ... }

           Here, I would either make the methods init() and next()
           ~const, or I would make the data members that record the
           state of the iterator ~const.

         * Internal buffer.
           I have a Trie class which represents a collection of
           strings.  When I want to retrieve any one of those strings,
           I need to write the result to a character buffer.  I want
           the user to be able to retrieve a string from a const Trie,
           but do not want him or her to have to worry about
           allocating the memory for the character buffer.  Answer: a
           ~const character buffer within the class.  (Incidentally,
           it must be resizable.)

         * Cache or secondary representation.
           This is a trumped-up example, because my real example is
           too application-specific.  Say you have an array class A.
           Based on your intended usage, you have decided it is best
           to store the elements in unsorted form.  But occasionally
           (very rarely) you will want to know the i'th, j'th and k'th
           element of some const A.  So you have decided to do some
           kind of sort, but only when it is needed.  It is
           appropriate to store the order information in ~const data
           members.

(2) Example of the convention I have used as a workaround:

    Here is what I do now to implement ~const member functions--

        void DynArray::grow() const
        {
            DynArray *const auxthis = (DynArray *const) this;
            // refer to all data through auxthis
        }

    The method is const because I want to be able to invoke it on a
    const object.  Here const signfifies that the object's meaning is
    unchanged by the method.  (The use of the word "auxthis" is
    historical.  Originally, instead of ~const I had proposed the
    addition of a new keyword "auxiliary" before Tony Hansen pointed
    out that this would break existing code.)

    Here is what I would do if I had ~const--

        void DynArray::grow() ~const { ... }

    I don't have a corresponding convention for ~const data members.
--
  Tom Ngo
  ngo@harvard.harvard.edu
  617/495-1768 lab number, leave message