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