Newsgroups: alt.lang.design,comp.lang.c++,comp.lang.lisp
Path: cantaloupe.srv.cs.cmu.edu!nntp.club.cc.cmu.edu!godot.cc.duq.edu!ddsw1!news.kei.com!bloom-beacon.mit.edu!usc!elroy.jpl.nasa.gov!decwrl!adobe!macb021.mv.us.adobe.com!user
From: mhamburg@mv.us.adobe.com (Mark Hamburg)
Subject: Re: Comparing productivity: LisP against C++ (was Re: Reference Counting)
Message-ID: <mhamburg-020195202723@macb021.mv.us.adobe.com>
Followup-To: alt.lang.design,comp.lang.c++,comp.lang.lisp
Sender: usenet@adobe.com (USENET NEWS)
Organization: Adobe Systems, Inc.
References: <D16Ho3.4BE@lcpd2.SanDiegoCA.NCR.COM> <3e2bj6$43a@network.ucsd.edu> <3e30ag$ar1@wariat.wariat.org> <19941231.180912.4252 <19950102.193610.775573.NETNEWS@UICVM.UIC.EDU>
Date: Tue, 3 Jan 1995 04:27:23 GMT
Lines: 118
Xref: glinda.oz.cs.cmu.edu comp.lang.c++:105601 comp.lang.lisp:16243

In article <19950102.193610.775573.NETNEWS@UICVM.UIC.EDU>,
dhanley@matisse.eecs.uic.edu (David Hanley) wrote:

> : This is not a red herring. The fact is that a large amount of C++
> : code uses reference counting schemes to manage memory.
> 
>         Care to document this "fact", as well as documenting that
> it makes the said code slower?

I can't document any particular percentages but I can point to books like
Coplien's which outline how to use reference counting for automatic storage
management. Working in C++, reference counting "smart" pointers have
certainly been my implementation mechanism of choice for data structures
since they can be implemented with essentially no compiler dependencies.

The reason for using "automatic memory management" in Photoshop was that it
allowed us to share data structures (particularly large data structures
representing images) without worrying about when to dispose of them. The
program before shifting to reference counting had to include code in most
destructors to figure out whether or not to destroy an image. For complex
operations, this was a pain to code and potentially buggy. Consider the
following old style code:

class AddChannelCommand: public Command
    {

    public:

        AddChannelCommand (ImageDocument *document, int index)
            {
            fDocument = document;
            fIndex = index;
            fChannelData = NewImageChannel ();
            }

        virtual ~AddChannelCommand ()
            {
            if (fChannelData != NULL) delete fChannelData;
            }

         virtual DoCmd ()
            {
            fDocument->InsertChannel (fIndex, fChannelData);
            fChannelData = NULL;
            }

         virtual UndoCmd ()
            {
            fChannelData = fDocument->RemoveChannel (fIndex);
            } 

    protected:
        ImageDocument *fDocument;
        int fIndex;
        ImageChannel *fChannelData;

    };

Here it is with reference counting:

class AddChannelCommand: public Command
    {

    public:

        AddChannelCommand (ImageDocument *document, int index)
            {
            fDocument = document;
            fIndex = index;
            fChannelData = NewImageChannel ();
            }

         virtual DoCmd ()
            {
            fDocument->InsertChannel (fIndex, fChannelData);
            }

         virtual UndoCmd ()
            {
            fChannelData = fDocument->RemoveChannel (fIndex);
            } 

    protected:
        ImageDocument *fDocument;
        int fIndex;
        PImageChannel fChannelData; // A smart pointer to an image channel.

    };

Gone is the need for the destructor (the destruction of fChannelData
handles this). Gone too is the need to NULL fChannelData after storing it
away. This was a simple example, so it was pretty clean both ways. I leave
it to the reader to imagine some of the more interesting cases.

Furthermore, if implementing undo using limited structure copying with data
sharing, this sort of automatic memory management becomes pretty important.

Now, then, why is reference counting inadequate?

1. We have to be careful about the sorts of linked structures we build.
Cycles create resource leaks with reference counting unless you actively
design some pointers to be non-counted. (Did you pick the right ones? Are
you sure?)

2. Calling functions that take reference counted pointers as parameters
results in a constructor call and a destructor call at minimum. If the
parameter is passed down through several levels this is a lot of excess
work. It's also excess code. (Remember we are programming in C/C++ and
avoiding GC because we are worried about performance.) The work arounds for
this are either to pass references to the counted pointers (dangerous if
the pointer actually referenced changes when we weren't expecting it) or to
pass around non-counted pointers (dangerous because now we have some
unmanaged references floating around).

I could go on, but that's enough gasoline for the fire for now.

Mark Hamburg
Adobe Systems, Inc.
