Newsgroups: comp.lang.c++
Path: utzoo!utgpu!craig
From: craig@gpu.utcs.utoronto.ca (Craig Hubley)
Subject: Re: asking an object for its type
Message-ID: <1991Mar2.232321.13459@gpu.utcs.utoronto.ca>
Organization: Craig Hubley & Associates
References: <23984@netcom.COM> <1190@sheol.UUCP> <70902@microsoft.UUCP> <6900@mace.cc.purdue.edu>
Date: Sat, 2 Mar 1991 23:23:21 GMT

In article <6900@mace.cc.purdue.edu> nvi@mace.cc.purdue.edu (Charles C. Allen) writes:
>In article <70902@microsoft.UUCP>, jimad@microsoft.UUCP (Jim ADCOCK) writes:
>> Smalltalk, C++ doesn't match methods based on name anyway.  Two independent
>> classes can both have a member "doSomething()" and the name matching
>> could be totally accidental, and mean totally different things, and
>> the two authors of those independent classes may have absolutely no
>> intention that their two classes be intermixed.  We don't want to reinvent
>> the Smalltalk situation where accidental matching of methods occur.
>
>Perhaps you could explain the exact problem here.  I agree that two
>"doSomething" methods can mean different things, I just don't
>understand the problem in the context of this discussion.

Not to speak for Jim, but two methods speed() could mean something very
different when applied to a ship (speed over water measured in knots) and
an airplane (airspeed measured in km/h).  Ideally one would be returning
values typed as knots and km/h, with conversions where (and if) appropriate.
However, now knowing that there is a member speed() means nothing unless you
know that it has a "speed in knots" or "knots speed()".  This means you have
to go past the name, to find out "what the name returns" or "what arguments
it takes" or "what class it comes from".  This latter is the most usual
solution, that is, I can differentiate "speed as a ship" from "speed as
a plane".

This is only possible in C++ now if you know that you are descended from
both.  And there is no way to find this out at runtime (ruling out changes
to the base classes).

I agree, by the way, that the "test for member" should include some way to
test the origin (base class) or protocol (return & arguments) of that member.

>> Since protocols are inherited from some base class, some people conceptualize
>> this slightly differently:
>> 
>> 	if (unidentifiedDeserializingObject->IsSomeSubClassOf(Class("FooBar")))
>> 	{
>> 		doFooBarThingsWith(unidentifiedDeserializingObject);
>> 	}

This forces FooBar to support the FooBarThings without exception, even though
C++ permits private (implementation) inheritance.  I assume that Jim means

"IsSomePublicSubClassOf(Class("FooBar"))".

>You seem to imply that Smalltalk cannot do this.  Smalltalk knows both
>the class of any given object and the class hierarchy, so it can
>certainly determine whether or not an object is "a kind of" FooBar:
>
>	anObject isKindOf: Foobar.

Certainly.  This is differentiating the "exact" type from the "base" types.
Things get a little more complicated when multiple inheritance is involved,
as others have pointed out the lookups can be more complex:  in general
knowing the possible acceptable types of the passed object a compiler can
resolve such a lookup down to no more than a virtual function call and possibly
to nothing: e.g. the above would compile to "true" if anObject was passed as a
Foobar... it would only be accepted if it was a Foobar or descendant of Foobar)

I agree in general with the idea of "protocols" but these can be implemented
in C++ now as pure virtual abstract base classes which are inherited publicly.
When I teach C++ this is the preferred way of sharing behavior, and I try to
untangle it from the implementation inheritance C++ tends to emphasize.  In
my view the "unified" hierarchy that factors both external behavior and
internal implementation is quite difficult to build beyond a few layers deep,
and keeping them separate permits quite a bit more flexibility to both the
producer and consumer.  Some languages, such as Trellis, take this to an
extreme and support implementation inheritance except as a default.  I 
believe this issue (whether to have separate behavior/implementation
hierarchies) has been beaten to death in comp.object, so I won't repeat it.
My own position is "yes, but sometimes you can safely avoid it" where the
definition of "safe" varies with circumstances.

C++ has a pseudo-protocol, the pure virtual abstract base class, consisting of
function signatures.  However, since in C++ inheriting classes cannot vary 
these signatures even in type safe ways (more specific return types, more 
general arguments), and cannot determine which of several possible protocols
is likely to be invoked (no type tag or base class lookup), or even if then
protocol being invoked is likely to cause an error, the utility of the 
protocol approach is severely (and IMHO unnecessarily) restricted.

With no way to specialize signatures or differentiate between protocols,
C++ programmers are more or less forced to add virtual functions one at
a time as they build subclasses.  When the signature must differ from that
defined in an earlier class, a new name must be invented.  Therefore the 
"protocol" will necessarily be larger, and spread across several classes 
rather than concentrated in one, as a direct consequence of the way C++
forces these classes to be built.  

Defining a true protocol in C++, with signatures that could be overridden
in type-safe ways, might overcome some of these problems and help make external
behavior more predictable and encourage polymorphism over brand-new functions
in each subclass.  It ain't everyone's cup o' tea, but it needn't inconvenience
them any.
-- 
  Craig Hubley   "...get rid of a man as soon as he thinks himself an expert."
  Craig Hubley & Associates------------------------------------Henry Ford Sr.
  craig@gpu.utcs.Utoronto.CA   UUNET!utai!utgpu!craig   craig@utorgpu.BITNET
  craig@gpu.utcs.toronto.EDU   {allegra,bnr-vpa,decvax}!utcsri!utgpu!craig
