Topic: Downcasting and mortal sin (was run-time type checking)


Author: maxtal@extro.ucc.su.OZ.AU (John MAX Skaller)
Date: Sat, 8 Aug 1992 00:18:52 GMT
Raw View
In my last post I alluded to the fact that taxonomies
(classification heirarchies like that used for animals)
are not the type of relationship that inheritance supports,
but I didn't remember why.

Well, the reason is simple: there cannot be any objects!
At no level in the classification heirarchy is there any individual
organism--every level is a category, and there is only one
instance of the category---the class itself.

Thus,

 class Animal {

 class Mammal : Animal
 class Bird  : Animal
 class Waterbird : Bird
 class Dog : Mammal

etc is all wrong in the first place. There cannot be
an animal with exact type 'Bird', nor ANY other class in
this scheme. Maybe thats why 'mate' and 'fight' will
never work!


--
;----------------------------------------------------------------------
        JOHN (MAX) SKALLER,         maxtal@extro.ucc.su.oz.au
 Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------




Author: maxtal@extro.ucc.su.OZ.AU (John MAX Skaller)
Date: Sat, 8 Aug 1992 00:12:51 GMT
Raw View
In article <1992Aug6.002718.28164@cadsun.corp.mot.com> shang@corp.mot.com writes:
>In article <2A7FD98D.736C@tct.com> chip@tct.com (Chip Salzenberg) writes:
>> It is wrong for the owner of two Animals, who wants to mate them, to
>> ask first "is this a Dog" and "is that a Dog".  Rather, the owner
>> should call virtual Dog::mate(), which uses its own means to determine
>> "is that something that I want to mate with?"
>>
>The owner must be very stupid. Why? He tries to put a wolf and a lamb together
>and let them to decide whether to mate or not. And he got a box which contains
>an animal. He said, "Oh, great! I'll put my parrot into the box. I just need
>some pretty parrot babies". And there happened to be a bird-eating cat in the
>box. Oooops, his parrot!

 Actually, David, I agree. The owner should KNOW what type
the animals are WITHOUT ASKING THEM. In other words, he has

 parrot &p1, &p2;

 And to mate them calls

 parrot::mate(parrot&);

He does not call animal::mate(animal&), such a function is not required.

But that was my ORIGINAL proposal: the zookeeper should keep track
of which animals are which WITHOUT ASKING THEM WHAT TYPE THEY
ARE, because animals cant talk!

There's only two ways to do this in C++ (without thisclass).
Either use polymorphism and have a totally general
mechanism to decide who mates and who doesnt (only a microbiologist
of the 23rd century could actually write this code), as
Chip suggests,

or

keep track of the animals an DONT use polymorphism, use the actual
object types.

>
>> In other words, use virtual functions to report _attributes_, not
>> _types_.  Then your abstractions will live long and prosper.
>>
>Types are the basic abstraction feature used to classify objects according to
>their attributes. Therefore, we should basically use types to report
>attributes, not virtual functions.

 This is strange: you dont *report* a type, you MUST
already know the type, your knowledge of the type determines
the operations you can do. If you only know something is
an animal, you can and should only be able to do things
to it that you can do to all animals. (Kill, getSex,
countLegs, etc)


>Then our abstractions will live long and
>proper!

 But you propose to BYPASS the abstraction and get at the
nitty gritty details NOT mentioned in the abstraction.
What purpose then does the abstraction serve if it does not
abstract common details and restrict you to access via those
common details?

>Virtual functions provides a polymorphism that may be very easily
>abused while it is not so easy to abuse a sound type system (either static or
>dynamic).

 Huh? Virtual functions ARE the sound type system.
Without them, there is NO polymorphism. Accessing exact type details
is counter to the concept of polymorphism as I understand it in
C++, C++ polymorphic objects are homogenous, not heterogenous--
they all look the same.

 A virtual function returning an int is EXACTLY the same
as a publically read only int variable. It is just that the method of
setting the variable is defered to the derived class.
>
>I will definitely NOT to put my pet into a your "mate" function, because you
>cannot ensure me that all the animals in derived classes are nicely
>implemented.
>
>David Shang

 Just as a matter of interest: we have been discussing
classification of animals, this is called taxonomy.
Taxonomies are one of the few cases that I have seen quoted
as being an example of where inheritance is totally the
wrong mechanism. (I'll try to find the quote and the reason).

--
;----------------------------------------------------------------------
        JOHN (MAX) SKALLER,         maxtal@extro.ucc.su.oz.au
 Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------




Author: jrobie@netmbx.netmbx.de (Jonathan Robie)
Date: Mon, 10 Aug 1992 07:28:39 GMT
Raw View
maxtal@extro.ucc.su.OZ.AU (John MAX Skaller) writes:

>In article <1992Aug6.002718.28164@cadsun.corp.mot.com> shang@corp.mot.com writes:
>>In article <2A7FD98D.736C@tct.com> chip@tct.com (Chip Salzenberg) writes:
>>> It is wrong for the owner of two Animals, who wants to mate them, to
>>> ask first "is this a Dog" and "is that a Dog".  Rather, the owner
>>> should call virtual Dog::mate(), which uses its own means to determine
>>> "is that something that I want to mate with?"
>>>

The lights have been dimmed, the champagne is gone, and there is a
faint rustling in the bedroom.

 "You animal!" cries a woman's voice.
 "We all *know* that!  We just can't agree whether we need
 the complete inheritance information!"
--
Jonathan

===========================================================================





Author: chip@tct.com (Chip Salzenberg)
Date: Thu, 30 Jul 1992 19:42:11 GMT
Raw View
According to shang@corp.mot.com:
>In article <2A75806A.826A@tct.com> chip@tct.com (Chip Salzenberg) writes:
>> virtual boolean Animal::can_mate_with(const Animal&);
>
>I'm interested in the implementation of your "can_mate_with" in a derived
>class. Whould you show me how it is done without RTTI?

Of course.

>(1) Do not supply your own RTTI through virtual functions.
>(2) Do not flood virtual functions in the base for each possible derived
>    classes.

Ah, so you already know how it can be done.

>Okay?

No, not okay.  I do not program with one hand tied behind my back.
The complete language is always at my disposal.
--
Chip Salzenberg at Teltronics/TCT  <chip@tct.com>, <73717.366@compuserve.com>
   "Do Rush place weird subliminal backmasked messages in their songs to
 compel unwilling geeks to commit evil .sig atrocities?"  -- Dean Engelhardt




Author: barmar@think.com (Barry Margolin)
Date: 30 Jul 1992 22:51:13 GMT
Raw View
In article <2A784613.34A4@tct.com> chip@tct.com (Chip Salzenberg) writes:
>According to shang@corp.mot.com:
>>In article <2A75806A.826A@tct.com> chip@tct.com (Chip Salzenberg) writes:
>>> virtual boolean Animal::can_mate_with(const Animal&);
>>
>>I'm interested in the implementation of your "can_mate_with" in a derived
>>class. Whould you show me how it is done without RTTI?

[Shang then puts a few other restrictions on him.]

>No, not okay.  I do not program with one hand tied behind my back.
>The complete language is always at my disposal.

OK, let me rephrase shang's challenge (and beat maxtal to the punch):

How can you implement can_mate_with() in such a way that the open/closed
principle is maintained?  This requires that you be able to add new derived
classes without having to modify the base class definition (which may be
shrink-wrapped, so the source is not available for modification).
--
Barry Margolin
System Manager, Thinking Machines Corp.

barmar@think.com          {uunet,harvard}!think!barmar




Author: maxtal@extro.ucc.su.OZ.AU (John MAX Skaller)
Date: Sat, 1 Aug 1992 19:22:35 GMT
Raw View
In article <159rp1INNcjc@early-bird.think.com> barmar@think.com (Barry Margolin) writes:
>In article <2A784613.34A4@tct.com> chip@tct.com (Chip Salzenberg) writes:
>>According to shang@corp.mot.com:
>>>In article <2A75806A.826A@tct.com> chip@tct.com (Chip Salzenberg) writes:
>>>> virtual boolean Animal::can_mate_with(const Animal&);
>>>
>>>I'm interested in the implementation of your "can_mate_with" in a derived
>>>class. Whould you show me how it is done without RTTI?
>
>[Shang then puts a few other restrictions on him.]
>
>>No, not okay.  I do not program with one hand tied behind my back.
>>The complete language is always at my disposal.
>
>OK, let me rephrase shang's challenge (and beat maxtal to the punch):
>
>How can you implement can_mate_with() in such a way that the open/closed
>principle is maintained?  This requires that you be able to add new derived
>classes without having to modify the base class definition (which may be
>shrink-wrapped, so the source is not available for modification).
>--
>Barry Margolin

 Netnews is relativistic. I wrote my reply to the above 2 days
before reading yours. So from my point of view I've already
beaten you to the punch, whereas from your's you sneaked in
(unless you missed my reply!).

 The answer is simple, of course, to use polymorphism.
That means you MUST virtualise the rules by which the decision
is made and put that interface in the base class.

 class Animal {

  virtual int nChromosomes()const=0;
  ....
  // other biological data required to
  // determine if two animals can mate
  // not being a biologist I cant fill these
  // in for you
 };

 This is the only way to do it, no other way can support
arbitrary derived classes.

 So in a sense, the open/closed principle IS polymorphism.
(In C++ applied at the class interface and implementation level).

 In particular is OBVIOUSLY cant be done with downcasting,
since you would need to put an arbitrary number of casts into
a finite piece of code.

 I'm assuming here the rule for mating is more complex
than just 'same species', since I already gave a solution for that.

--
;----------------------------------------------------------------------
        JOHN (MAX) SKALLER,         maxtal@extro.ucc.su.oz.au
 Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------




Author: shang@corp.mot.com (David (Lujun) Shang)
Date: Thu, 6 Aug 92 00:27:18 GMT
Raw View
In article <2A7FD98D.736C@tct.com> chip@tct.com (Chip Salzenberg) writes:
> It is wrong for the owner of two Animals, who wants to mate them, to
> ask first "is this a Dog" and "is that a Dog".  Rather, the owner
> should call virtual Dog::mate(), which uses its own means to determine
> "is that something that I want to mate with?"
>
The owner must be very stupid. Why? He tries to put a wolf and a lamb together
and let them to decide whether to mate or not. And he got a box which contains
an animal. He said, "Oh, great! I'll put my parrot into the box. I just need
some pretty parrot babies". And there happened to be a bird-eating cat in the
box. Oooops, his parrot!

> In other words, use virtual functions to report _attributes_, not
> _types_.  Then your abstractions will live long and prosper.
>
Types are the basic abstraction feature used to classify objects according to
their attributes. Therefore, we should basically use types to report
attributes, not virtual functions. Then our abstractions will live long and
proper! Virtual functions provides a polymorphism that may be very easily
abused while it is not so easy to abuse a sound type system (either static or
dynamic).

I will definitely NOT to put my pet into a your "mate" function, because you
cannot ensure me that all the animals in derived classes are nicely
implemented.

David Shang




Author: chip@tct.com (Chip Salzenberg)
Date: Wed, 5 Aug 1992 13:37:17 GMT
Raw View
According to mat@mole-end.matawan.nj.us:
>According to shang@corp.mot.com:
>>(1) Do not supply your own RTTI through virtual functions.
>>(2) Do not flood virtual functions in the base for each possible derived
>>    classes.
>
>The reason for these restrictions is that the two options described
>are costly.

Sure.  But they approximate the real solution closely enough that
Shang might not have seen the difference, and I didn't feel like
trying to convince a zealot.

To paraphrase Maxtal, RTTI often provides the precise answer to the
wrong question.

It is wrong for the owner of two Animals, who wants to mate them, to
ask first "is this a Dog" and "is that a Dog".  Rather, the owner
should call virtual Dog::mate(), which uses its own means to determine
"is that something that I want to mate with?"

In other words, use virtual functions to report _attributes_, not
_types_.  Then your abstractions will live long and prosper.

>Every class must bear the burden...

But the burden need not be heavy.  Virtual functions need only be
defined when their base class implementations are inappropriate.
Carefully chosen base class implementations need less overriding.

>RTTI can be provided by the compiler at little compile-time and run-time
>cost, and almost no cost to the programmer.

I wouldn't cry if RTTI were introduced.  I'd probably even use it in
moderation.  But I anticipate that it will be grossly overused by
Smalltalk-influenced programmers, and clean data abstraction will be
the first casualty.
--
Chip Salzenberg at Teltronics/TCT  <chip@tct.com>, <73717.366@compuserve.com>
   "Do Rush place weird subliminal backmasked messages in their songs to
 compel unwilling geeks to commit evil .sig atrocities?"  -- Dean Engelhardt




Author: chip@tct.com (Chip Salzenberg)
Date: Thu, 6 Aug 1992 20:19:34 GMT
Raw View
According to shang@corp.mot.com:
>Types are the basic abstraction feature used to classify objects according
>to their attributes. Therefore, we should basically use types to report
>attributes, not virtual functions.

Could someone please explain "abstraction" to Mr. Shang?

I'll be on vacation for a week.  I hope that when I get back he'll
understand why "using types to report attributes" is a silly idea.
--
Chip Salzenberg at Teltronics/TCT  <chip@tct.com>, <73717.366@compuserve.com>
   "Do Rush place weird subliminal backmasked messages in their songs to
 compel unwilling geeks to commit evil .sig atrocities?"  -- Dean Engelhardt




Author: jrobie@netmbx.netmbx.de (Jonathan Robie)
Date: Mon, 27 Jul 1992 08:50:51 GMT
Raw View
glw@io.uswest.com (Glenn Williams) writes:

>>(John MAX Skaller) writes:

>>class Animal {
>> int mate(Animal&);
>>};
>>class Dog : Animal {
>> int mate (Animal&);
>>};

>>We REALLY want Dog::mate(Dog&), but we can't have it.

I'm confused.  I have two animals, and I place them in a cage.
Who knows where these animals come from or what kind they are--
my classes may not have produced them.

Now the program needs to decide if these animals should mate.
I assume that the program will follow the general biological rule
and restrict mating activity to animals of the same type.

I think that we need virtual functions to do this right.  Otherwise
a dog accessed with an Animal pointer will mate any other animal,
but you can't even ask the dog if he wants to mate with anything
other than a dog if you access it with the Dog pointer.

I would think that the program should let the dog decide if this
other animal is the kind he likes.  The mating function might also
be virtual since different animals have different habits--you can't
expect dogs to practice safe sex, for instance.  So the mate() function
for a dog must be Dog::mate().

This seems to need a down cast.

So does any procedure that would attempt to determine if one animal
is the same kind as another.



Jonathan

===========================================================================

Jonathan Robie  jrobie@netmbx.UUCP
Arnold-Zweig-Str. 44 jrobie@netmbx.in-berlin.de
O-1100 Berlin
Deutschland  Phone:  +37 (2) 472 04 19  (Home, East Berlin)
    +49 (30) 342 30 66 (Work, West Berlin)


--
Jonathan

===========================================================================





Author: chip@tct.com (Chip Salzenberg)
Date: Tue, 28 Jul 1992 17:14:17 GMT
Raw View
According to jrobie@netmbx.netmbx.de (Jonathan Robie):
>I'm confused.  I have two animals, and I place them in a cage.
>Who knows where these animals come from or what kind they are--
>my classes may not have produced them.

This is an important point: Further derivations of Animal may occur
after all of "our" code is written and stashed in a library.

>Now the program needs to decide if these animals should mate.

No!

The program may decide whether it _wants_ the animals to mate.  But as
for deciding the results of such an attempt, only the animals know!
This fact is a consequence of the "who should know" principle: animals
may make mating decisions based on factors that I can't imagine ahead
of time.

Therefore you need this:

    virtual boolean Animal::can_mate_with(const Animal&);

Then there are can be any number of virtual member functions that each
implementation of SomeAnimalType::can_mate_with() may use to determine
if any given OtherAnimalType is suitable for mating.  Some classes
derived from Dog may be more picky than others, for example.

Presto!  No RTTI, and it still works.

Remember the golden rule of software interfaces:

   Never try to predict success or failure of an operation.
   Instead, try it and see what happens.

--
Chip Salzenberg at Teltronics/TCT  <chip@tct.com>, <73717.366@compuserve.com>
   "Do Rush place weird subliminal backmasked messages in their songs to
 compel unwilling geeks to commit evil .sig atrocities?"  -- Dean Engelhardt




Author: shang@corp.mot.com (David (Lujun) Shang)
Date: Wed, 29 Jul 92 16:43:43 GMT
Raw View
In article <2A75806A.826A@tct.com> chip@tct.com (Chip Salzenberg) writes:

> Therefore you need this:
>
>     virtual boolean Animal::can_mate_with(const Animal&);
>

I'm interested in the implementation of your "can_mate_with" in a derived
class. Whould you show me how it is done without RTTI?  Two conventions:

(1) Do not supply your own RTTI through virtual functions.
(2) Do not flood virtual functions in the base for each possible derived
classes.

Okay?

David Shang