From: jac@moonshine.llnl.gov (James A. Crotinger)
Newsgroups: comp.sys.amiga.programmer
Subject: Re: Obj-C vs. C++ [was Re: Comeau C++]
Date: 4 Oct 92 07:10:18 GMT
Organization: Magnetic Fusion Energy - LLNL

dnavas@oracle.uucp (David Navas) writes:
> >  This is not true. C++ does have run-time binding, and it is type-safe.

> C++'s run-time binding is only valid when the objects concerned have a
> common subclass.  In this case, type-checking is possible.  However,
> requiring both type-checking AND common subclasses (even noting that
> C++ has multiple inheritence) is a rather large impediment to creating
> system subclassable class definitions.

  Why?

> With SHADOW I can create a selector without ever declaring what classes
> it may or may not belong to, or even what arguments it may or may not
> take.

  Experience has put me strongly in the type-safe camp, and so I can't
imagine writing any large application in a language which allowed things
like this. But, to each his own. 

> I'm not sure how C++ handles default argument differences with
> virtual functions -- if it does, it's doing quite a bit of work, if it
> doesn't, then it's basically useless for what I need to get done.

  This is discouraged. Default arguments are determined statically,
not dynamically, and so they are not advised in virtual functions. 

> >Suppose class D inherits from B, which declares a "virtual function" 
> >named "foo". Class D can provide its own definition of "foo". Now,

> And to call D's superclass' definition of foo we'd just type-cast right?
> So what happens if D shifts within the hierarchy?

  No. If you want to explicitly call D::foo, you just refer to it
that way:

   D d; 
   B *bp = &d; 

   bp->foo()      // calls D::foo()
   d.foo()        // ditto
   bp->B::foo()   // calls B::foo()
   d.B::foo()     // ditto

A fairly common idiom is for D::foo() to call B::foo() in its implementation:

   D::foo() 
   {
      B::foo();
      // do some other stuff...
   }

> >This is all type safe! The compiler has seen the declarations of
> >B and D. It knows that the B class understands the message "foo",
> >and that any class which publicly inherits from B will also understand
> >this message.

> Yes, but it understands the message only in that the message must be
> exactly equivalent.  This is not always true in real life.
> Consider the OpenWIndow() calls in Intuition.  We have OpenWindowTags()
> and OpenWindow() which are two separate definitions of the same selector --
> OPEN_WINDOW.  Depending on the class (V1.3 or V2.0) you're sending the
> message to, different arguments are required.  How do you type-check in
> cases like this?

  I believe you can overload virtual functions:

      class B {
        public:
          virtual foo(int);
          virtual foo(double);
      };

      class D: public B {
        public:
          foo(int);
          foo(double);
      };

The entire signature is used to "name" the function. Dispatching
occurs after this. 

Thus you could have an OpenWindow message which took the old style set
of arguments, and another which took a TagList.

> >However, what would happen if pb pointed to a B? This is not a safe
> >program. You might say that you'd only do this if you knew that pb
> >pointed to a D. Well, if you do know this then you can cast the pb to

> Nope, you've confined yourself to working within C++ too long, and are
> using an argument which rationalizes the way C++ works. :)

> If pb pointed to a B, then the message would be ignored as it isn't
> impemented.  This kind of behaviour is sometimes used for callbacks.

  Well, like I said, I think this type of thing would be a nightmare in
a language that you wanted to use for a large application. Don't get me
wrong, I think there are good uses for "shoot from the hip" languages.
When speed of the final production code isn't a major design
consideration, then (for me) optimizing programmer efficiency is, and
for small projects I like to work in languages that give me a lot of
freedom. In such cases, C++ might not be my first choice.

> Now, in C++ you could change the definition of B to support this as well --
> if you have appropriate access to the source.  Think Cmdre would give out its
> OS source?  Nah, didn't think so.

  No. You just make a subclass of Commodore's classes as your base class
tree. Also, multiple inheritance can be quite handy for "mixin"
programming, where you want to add features to a subtree of a class
hierarchy.

> How difficult is it to have C++ create objects in, say, CHIP_MEM?  Or
> allocate objects from a private memory pool?

  This is not a problem. You can overload operator new() and delete
globally and at the class level. You can also do things like define
versions of new with different signatures:

    class ChipType {};

    void *operator new(size_t size, ChipType) 
      { 
         // allocate size bytes in chip memory
      }

    ChipType CHIP;

This has a second argument of type ChipType, which is used only to 
distinguish it from the default new. This is called as:

    MyImage *im = new(CHIP) MyImage;

It is probably better to overload operator new() at the class level for
those classes which have special memory needs. You do have to be careful
when doing so since FreeMem requires that you remember the size of
memory which needs freed, and C++ doesn't remember it for you. If you're
only targeting 2.0, though, you can use AllocVec and FreeVec, and not
have to worry about this wrinkle.

If you need more efficient memory allocators, again it can be done via
overloaded operator new(). 

> It might not be a bad idea to attend Taligent's meeting (sometime in early
> October).  They had to dump C++ (they have some bizarre notion about creating
> their own object-oriented C derivative last I heard) for some reason.  It
> might be interesting to get their perspective....

  Ack! I don't know what their needs are, so I can't judge their
decision.  However, I've known several C programmers who have given up
on C++ because they wouldn't take the time to learn the language, and
then they've gone and re-invented the wheel by developing their own OO
scheme of C programming, which is usually much more error prone and gives
up much of C++'s safety.

> Probably not.  One of the advantages of object-oriented code is the
> layer it creates between implementation and calling mechanisms.  I'm
> not so sure that layer is real thick if I need to go and rehack base classes
> just to provide some polymorphic messages I need for subclasses.  In
> additions, it would be nice to only have to ship -partial- definitions
> of structures to the end-programmer -- ie: only the parts that they
> can read or read/write.  While public/private is a really nice feature,
> it gives away a little too much of the "magic" for my tastes....

  This is an area of C++ which can be a problem. You can, of course,
program around the problem by encapsulating the implementation of a 
class in a separate class:

        class Foo {
          private:
             FooImplementation *_f
          public:
             // Foo's interface
        }

The header file for FooImplementation does not even need to be shipped.
This scheme, of course, is not as efficient as having the implementation
in-line. However, it may be as efficient as other languages which manage
to hide their implementations using similar, but hidden, tricks.

I know that this was a design issue with BOOPSI: to be able to upgrade
the dynamic libraries without requiring people to recompile their codes
requires that the implementation be hidden. I think this could be
accomplished in C++ using the above idiom.

  Jim

--
-------------------------------------------------/\--------------------------
James A. Crotinger     Lawrence Livermore N'Lab // \ The above views are mine
jac@moonshine.llnl.gov P.O. Box 808;  L-630 \\ //---\  and are not neces-
(510) 422-0259         Livermore CA  94550   \\/Amiga\  sarily those of LLNL.
Newsgroups: comp.sys.amiga.programmer