kearns@softrue.UUCP (Steven Kearns) (03/29/91)
There has been much discussion lately about adding stuff to C++ so that people can assign a Base* to a Derived* without a cast. People feel "guilty" when they use a cast, as if they are doing something unsafe. If you look at things from a "program verification" viewpoint, the issue becomes much clearer. A cast of (Base*) to Derived* is just a function call that assumes that the Base* really points to a Derived*; if you can prove to yourself (and God) that this is so, then using the cast is perfectly safe, and you should not feel guilty. If we added stuff to C++ to take care of this "problem", then we would also have to add new types so that people do not call sqrt(x) when x is a negative number, or so they do not call a/b when b is 0. -steve ******************************************************** * Steven Kearns ....uunet!softrue!kearns * * Software Truth softrue!kearns@uunet.uu.net * ********************************************************
cok@islsun.Kodak.COM (David Cok) (03/31/91)
In article <17.UUL1.3#8618@softrue.UUCP> kearns@softrue.UUCP (Steven Kearns) writes: >There has been much discussion lately about adding stuff to C++ >so that people can assign a Base* to a Derived* without a cast. >People feel "guilty" when they use a cast, as if they are doing something >unsafe. > >If you look at things from a "program verification" viewpoint, the issue >becomes much clearer. A cast of (Base*) to Derived* is just a function >call that assumes that the Base* really points to a Derived*; if you >can prove to yourself (and God) that this is so, then using the cast >is perfectly safe, and you should not feel guilty. And if you can prove that global variables are used correctly, and goto's, and case statements or types, you should not feel guilty??? Some constructs are error-inviting -- cast is one of them -- and are best avoided. If you need to use them, you have to be very careful, but if a program has a lot of them bugs are likely. Current C++ can hardly be used for some kinds of things (at least the alternatives to casting are just as bad) without a lot of Base* to Derived* casts. It would add to the robustness and strong-typing nature of the language to have type-safe downcasts. > >If we added stuff to C++ to take care of this "problem", then we would >also have to add new types so that people do not call sqrt(x) when >x is a negative number, or so they do not call a/b when b is 0. > No, you don't have to. Every language is a trade-off between what is checked at compile-time, what is checked at run-time, and what is left to cause core dumps, often at places far from the original problem. People differ as to what should be in which category -- witness the whole static vs. dynamic typing debate. Numeric range problems have traditionally been left to either fault (at least at the location where they occur), or to cause a run-time exception. I think that a type-safe downcast is a legitimate thing to consider supporting at run-time. >-steve > >******************************************************** >* Steven Kearns ....uunet!softrue!kearns * >* Software Truth softrue!kearns@uunet.uu.net * >******************************************************** David R. Cok Eastman Kodak Company -- Imaging Science Lab cok@Kodak.COM
kearns@softrue.UUCP (Steven Kearns) (04/02/91)
> From: cok@islsun.Kodak.COM (David Cok) > Subject: Re: typesafe downward casting > .... >And if you can prove that global variables are used correctly, and goto's,and > case statements or types, you should not feel guilty??? Some constructs are > error-inviting -- cast is one of them -- and are best avoided. If you need > to use them, you have to be very careful, but if a program has a lot of them > bugs are likely. Yes, if you can prove that your goto is correct, you should feel very satisfied. The only reason to avoid these statements, as Djikstra would say, is because they complicate the proof of correctness. It seems to me that knowing that a Base* actually points to a Derived* is about as trivial as knowing that a number is non-negative. If you cannot handle this, I suggest taking up a different profession ;-}. > >If we added stuff to C++ to take care of this "problem", then we would > >also have to add new types so that people do not call sqrt(x) when > >x is a negative number, or so they do not call a/b when b is 0. > > > No, you don't have to. Every language is a trade-off between what is checked > at compile-time, what is checked at run-time, and what is left to cause core > dumps, often at places far from the original problem. People differ as to > what should be in which category -- .... > I think that a type-safe downcast is a legitimate thing to consider > supporting at run-time. That is the point: there is no compelling reason to include it in the language except for your opinion. Why make type-safe downcasts but non-type-safe division? Fortunately, C++ gives you enough tools so that you can ensure type-safe downcasting if you want, while I can avoid them if I want. -steve ******************************************************** * Steven Kearns ....uunet!softrue!kearns * * Software Truth softrue!kearns@uunet.uu.net * ********************************************************
cok@islsun.Kodak.COM (David Cok) (04/03/91)
In article <18.UUL1.3#8618@softrue.UUCP> kearns@softrue.UUCP (Steven Kearns) writes: >> From: cok@islsun.Kodak.COM (David Cok) >> Subject: Re: typesafe downward casting >> .... >>And if you can prove that global variables are used correctly, and goto's,and >> case statements or types, you should not feel guilty??? Some constructs are >> error-inviting -- cast is one of them -- and are best avoided. If you need >> to use them, you have to be very careful, but if a program has a lot of them >> bugs are likely. > >Yes, if you can prove that your goto is correct, you should feel very >satisfied. The only reason to avoid these statements, as Djikstra >would say, is because they complicate the proof of correctness. It >seems to me that knowing that a Base* actually points to a Derived* >is about as trivial as knowing that a number is non-negative. If you >cannot handle this, I suggest taking up a different profession ;-}. > No, you should feel satisfied only if it is correct and is a good design. One measure of goodness of design is the speed with which a person unfamiliar with the code can justify its correctness -- and gotos generally, but not always, make that harder. Global variables require being aware of a lot more information than local variables -- and hence complicate (slow down) verifying correctness. I would maintain that uncheckable casts fall into the same category, and that a good program generally has few casts and a good programming language allows most programs to be written without casts. Knowing that a Base* points to a Derived* is about as trivial as knowing that a number is non-negative: both are undecidable in general at compile-time. At least C++ provides a nice little run-time test to see if a number is non-negative; it does not provide that for casting (except in a messy way -- see below). >> >If we added stuff to C++ to take care of this "problem", then we would >> >also have to add new types so that people do not call sqrt(x) when >> >x is a negative number, or so they do not call a/b when b is 0. >> > >> No, you don't have to. Every language is a trade-off between what is checked >> at compile-time, what is checked at run-time, and what is left to cause core >> dumps, often at places far from the original problem. People differ as to >> what should be in which category -- .... >> I think that a type-safe downcast is a legitimate thing to consider >> supporting at run-time. > >That is the point: there is no compelling reason to include it in the >language except for your opinion. Why make type-safe downcasts but >non-type-safe division? Fortunately, C++ gives you enough tools so >that you can ensure type-safe downcasting if you want, while I can >avoid them if I want. > But there is no compelling reason to leave it out except for your opinion :-) I'll take "opinion" here to be a position on language design based on some rational considerations -- on which reasonable people can disagree and can discuss without resorting to ad hominem arguments. Current C++ can supply type-safe downcasting only with (a) source code access to base classes, including libraries, and (b) building into base classes knowledge of each derived class. I maintain that each of those individually is too big a price to pay for what is a straightforward language feature. David R. Cok Eastman Kodak Company -- Imaging Science Lab cok@Kodak.COM
chip@tct.com (Chip Salzenberg) (04/04/91)
According to cok@islsun.Kodak.COM (David Cok): >I would maintain ... that a good program generally has few casts and a >good programming language allows most programs to be written without casts. Indeed. And that is true of C++ today -- IF you are willing to avoid using Base* all over the place when Derived* is meant. (Template collections are a tremendous help.) In other words, the way to regain an object's lost static type is not to lose it in the first place. -- Chip Salzenberg <chip@tct.com>, <uunet!pdn!tct!chip> Brand X Industries Custodial, Refurbishing and Containment Service When You Never, Ever Want To See It Again [tm]
cok@islsun.Kodak.COM (David Cok) (04/05/91)
In article <27FA1EC4.511C@tct.com> chip@tct.com (Chip Salzenberg) writes: >According to cok@islsun.Kodak.COM (David Cok): >>I would maintain ... that a good program generally has few casts and a >>good programming language allows most programs to be written without casts. > >Indeed. And that is true of C++ today -- IF you are willing to avoid >using Base* all over the place when Derived* is meant. (Template >collections are a tremendous help.) > >In other words, the way to regain an object's lost static type is not >to lose it in the first place. >-- I am certainly looking forward to having an environment that supports templates (no such luck here yet), and think it will do great things for reusing implementations of Lists, etc., but I don't think it is a replacement for inheritance. If you use inheritance simply to provide a new implementation of a class, you can keep manipulating the Base* values, and never need to deal with the Derived class at all except to state in a declaration which derived class to use, e.g. List* list = new MySpecialListImplementation. But another use of inheritance is to add functionality to an existing class by deriving from it. Now virtual functions which returned Base* will still return Base* in the derived class -- so I cannot help losing the object's static type. In this context one must either have contravariance on the return type of virtual functions or type-safe down casting -- or resort to current C++ which provides only type-unsafe down casting or corrupting the base class with the interface to the added functionality of the derived class. David R. Cok Eastman Kodak Company -- Imaging Science Lab cok@Kodak.COM
daves@ex.heurikon.com (Dave Scidmore) (04/07/91)
cok@islsun.Kodak.COM (David Cok): >I would maintain ... that a good program generally has few casts and a >good programming language allows most programs to be written without casts. Since C, and for the most part C++ as well, are not strongly typed languages and rely on casts to allow the programmer to make explicit changes in type, I would disagree with the above statement. Automatic or built in changes of type are essentially hidden rules which rely on the programmers intimate knowledge of the type structure and possibly even an intimate knowledge of the program to understand what is really happening. As a rule, I tend to like people who write programs I might be required to maintain to make as explicit as possible any subtle changes of type which I might not immediately grasp on first examination of the code. Casts are a means of making clear to other programmers what the original programmer intended. In the absence of casts when hunting down a bug I must constantly ask myself if the original programmer intended the "hidden" type conversion to occur, or whether the programmer was unaware that the type conversion could be taking place. When a cast is provided I have a much higher degree of confidence that what I see happening in the code was what was actually intended. In other words the authors of C provided casts as an alternative to the straight jacket form of type checking that Pascal has. Attempts to provide "hidden" type conversions can easily be used to erode that form of type checking the language provides. Erosion of that type checking provides more pitfalls into which programmers can fall. -- Dave Scidmore, Heurikon Corp. dave.scidmore@heurikon.com
chip@tct.com (Chip Salzenberg) (04/08/91)
According to cok@islsun.Kodak.COM (David Cok): >I am certainly looking forward to having an environment that supports >templates (no such luck here yet), and think it will do great things >for reusing implementations of Lists, etc., but I don't think it is a >replacement for inheritance. That's a straw man; no one has suggested otherwise. >If you use inheritance simply to provide a new implementation of a class, you >can keep manipulating the Base* values, and never need to deal with the >Derived class at all except to state in a declaration which derived class to >use, e.g. List* list = new MySpecialListImplementation. That's my most common use. >But another use of inheritance is to add functionality to an existing class by >deriving from it. Now virtual functions which returned Base* will still >return Base* in the derived class -- so I cannot help losing the object's >static type. In this context one must either have contravariance on the >return type of virtual functions or type-safe down casting ... Or (another choice): modify the base class for the added functionality (add a do-nothing virtual function, etc). That's my choice. -- Brand X Industries Custodial, Refurbishing and Containment Service: When You Never, Ever Want To See It Again [tm] Chip Salzenberg <chip@tct.com>, <uunet!pdn!tct!chip>
cok@islsun.Kodak.COM (David Cok) (04/09/91)
In article <28007BC8.D71@tct.com> chip@tct.com (Chip Salzenberg) writes: >According to cok@islsun.Kodak.COM (David Cok): .... > >>If you use inheritance simply to provide a new implementation of a class, you >>can keep manipulating the Base* values, and never need to deal with the >>Derived class at all except to state in a declaration which derived class to >>use, e.g. List* list = new MySpecialListImplementation. > >That's my most common use. > >>But another use of inheritance is to add functionality to an existing class by >>deriving from it. Now virtual functions which returned Base* will still >>return Base* in the derived class -- so I cannot help losing the object's >>static type. In this context one must either have contravariance on the >>return type of virtual functions or type-safe down casting ... > >Or (another choice): modify the base class for the added functionality >(add a do-nothing virtual function, etc). That's my choice. >-- You left off the rest of my statement: >> ... or corrupting the base class with the interface to the added >> functionality of the derived class. I just can't see that modifying the base class is a generally reasonable alternative -- even if you do have source code access, which in general you do not. Why should the added functionality of derived classes have to be propagated up the inheritance tree? That seems to violate all principles of modularity in designing classes, causes excessive code bloat and puts all derived class function names in the same name space. David R. Cok Eastman Kodak Company -- Imaging Science Lab cok@Kodak.COM
Ari.Huttunen@hut.fi (Ari Juhani Huttunen) (04/09/91)
In article <28007BC8.D71@tct.com> chip@tct.com (Chip Salzenberg) writes: >>But another use of inheritance is to add functionality to an existing class by >>deriving from it. Now virtual functions which returned Base* will still >>return Base* in the derived class -- so I cannot help losing the object's >>static type. In this context one must either have contravariance on the >>return type of virtual functions or type-safe down casting ... >Or (another choice): modify the base class for the added functionality >(add a do-nothing virtual function, etc). That's my choice. Is it still your choice if the base class is provided only in binary form? For example, a commercial library of classes for which you have no source code, except the header files. -- Ari Huttunen, email: Ari.Huttunen@hut.fi, phone: 90-7285944
chip@tct.com (Chip Salzenberg) (04/11/91)
According to Ari.Huttunen@hut.fi (Ari Juhani Huttunen): >>Somebody writes: >>>But another use of inheritance is to add functionality to an existing >>>class by deriving from it. ... In this context one must either have >>>contravariance on the return type of virtual functions or type-safe >>>down casting ... > >In article <28007BC8.D71@tct.com> chip@tct.com (Chip Salzenberg) writes: >>Or (another choice): modify the base class for the added functionality >>(add a do-nothing virtual function, etc). That's my choice. > >Is it still your choice if the base class is provided only in binary form? If I only have binaries, I refuse to inherit. I compose instead. It saves everyone a lot of trouble. -- Brand X Industries Custodial, Refurbishing and Containment Service: When You Never, Ever Want To See It Again [tm] Chip Salzenberg <chip@tct.com>, <uunet!pdn!tct!chip>