Path: utzoo!utgpu!news-server.csri.toronto.edu!rpi!zaphod.mps.ohio-state.edu!caen!spool.mu.edu!snorkelwacker.mit.edu!paperboy!paperboy.osf.org!pjg From: pjg@daedalus.osf.org (Paulo Guedes) Newsgroups: comp.lang.c++ Subject: Re: About Lists and things... Message-ID: Date: 24 Jun 91 21:29:03 GMT Article-I.D.: daedalus.PJG.91Jun24172903 References: <285E75EC.37E7@tct.com> <18982@prometheus.megatest.UUCP> <2865E7A8.179A@tct.com> Sender: news@OSF.ORG Organization: Open Software Foundation Lines: 85 In-reply-to: chip@tct.com's message of 24 Jun 91 12:38:00 GMT In article <2865E7A8.179A@tct.com> chip@tct.com (Chip Salzenberg) writes: [...] > My suggestions had been enumerated before. But, to repeat: > > Never design code that requires downcasts. > Or, in other words: if the static type is important, why worry > about regaining it? Don't lose it in the first place. > > Use templates. > If real templates are unavailable, use the preprocessor. > > Never, ever create "isa" or "typeof" or "classname" functions for > any purpose other than debugging. Any time you would depend on > such a function, create a virtual function that does what you > really wanted done in the first place. While I agree with your suggestions and think they should be part of normal programming practice, I think there are some kinds of problems where they don't apply. Let me give you an example derived from my present work: I have a system composed by several servers. Each server provides the implementation for a number of objects. Clients communicate with servers by RPC. The interfaces to the servers are defined by C++ (abstract) classes. As the result of an operation on a server, an object may be returned to the client. One of the servers is the file server, which implements files, directories, symbolic links, mount points, etc. Without going into much detail about their interface, all these objects share a common interface (e.g. all of them can be stat'ed), but some provide operations that others don't (e.g. a file can be read and written, but a mount point can't). The basic interface is defined in class Base. Class File derives from Base and adds the operations specific only to files, class Directory derives from Base and adds the operations specific only to directories, etc. Now, the problem: when I lookup an object by name obj = open ("someName") what type should open return ? It can only return type Base, because at compile time we cannot make any stronger guarantees on the type of the object that will be returned. However, the real type of the object will be File, Directory, etc, and *it will be known only at run-time*. I can use the object as a Base, but not as a File or Directory. In some cases this is ok (e.g. if I'm just querying its creation time) but not in others (e.g. read, write). Hence, I need some way of converting the reference from Base to File, Directory, etc.[*] It would be great if C++ offered me this (as Eiffel and Modula-3 do, as far as I understand), otherwise I just have to create my own (which I did, based on Keith Gorlen's NIHCL). Note that templates don't help here, because this is one case where a "list" (directory) contains objects of many different types (files, mount points, symbolic links, etc). Virtuals could solve the problem, but than, what would happen ? All the methods would end-up in the base class, with dummy implementations in the derived classes that would return simply "MethodNotImplemented". If this solution was adopted, static type checking would almost disapear because all methods would be valid ! The only errors detected would be calls to non-existing methods and methods called with wrong parameters, but errors like performing operations on a file that are only valid on directories would be detected only at run-time. Even worse, each time someone invented a new type of object at the server, the class hierarchy had to be changed to include its new methods. In client-server programming, you don't want to change your clients to cope with changes in the servers. In summary, avoiding downcasts is good programming practice, but we cannot ignore that some problems just don't fit in this model (specially when they are *my* problem ! :-) Paulo Guedes [*] A similar problem and conclusion was presented in "Adding new code to a running C++ program" by S. Dorward, R. Sethi and J. Shopiro in the 1990 C++ Usenix Conference.] -- --- Paulo Guedes Email: pjg@osf.osf.org Phone: (617) 621-8878 OSF Research Institute 11 Cambridge Center, Cambridge, MA 02142