Path: utzoo!utgpu!news-server.csri.toronto.edu!rpi!zaphod.mps.ohio-state.edu!swrinde!cs.utexas.edu!uunet!microsoft!jimad From: jimad@microsoft.UUCP (Jim ADCOCK) Newsgroups: comp.lang.c++ Subject: Re: Seeking neat way to do binary "virtual" functions. Message-ID: <71518@microsoft.UUCP> Date: 25 Mar 91 22:07:12 GMT References: <1991Mar19.200816.10480@kodak.kodak.com> Reply-To: jimad@microsoft.UUCP (Jim ADCOCK) Distribution: comp Organization: Microsoft Corp., Redmond WA Lines: 100 In article <1991Mar19.200816.10480@kodak.kodak.com> cok@islsun.Kodak.COM (David Cok) writes: |A similar problem arises in virtual functions which return pointers to the |base type. In the derived class, they must also return pointers to the |base class, when often one wants the derived class function to return a |pointer to a derived class object. Again, one can provide this in C++ by |adding an additional helper function, but it requires source code access to |the Base class, which to my mind should not be necessary. I also believe that |this is a lack in the language: it should allow contravariace on the function |return type and this problem would go away. Again this solution does not |break existing programs, does not add keywords, and would make some of my |programs 10s of percent more concise. If we look at the contravariance issue for a few moments, I think you'll see that it ties in with a lot of the recent talk about run-time type information and type-casting. First, what would it mean to support contravariance on the return type for C++? I don't think you're much interested in returning a Derived where the Base class specifies a Base -- you're really interested in returning a Derived* or Derived& where the Base class specifies a Base* or a Base&. What would it take to support this? At first thought the problem seems trivial -- until one considers multiple inheritence with virtual functions: Base { .... public: virtual Base* doSomething(); .... }; void usesDoSomething(Base* bp) { bp = bp->doSomething(); bp = bp->doSomething(); } class Derived: public Foo, public Base { Derived* doSomething(); } Do you see the problem? Since usesDoSomething(Base*) occurs before Derived, today's compilers would assume that the address returned from doSomething() the first time represents the address to dispatch relative to the second time. However, in Derived the Base part is typically generated as the second part of the Derived structure -- thus the wrong part of Derived is being referred to by bp in order to correctly dispatch doSomething() the second time. This problem could be solved if the compiler automatically generated code something like as follows: void usesDoSomething(Base* bp) /* as automatically generated by the compiler */ { bp = (bp->doSomething())->runtimeCastToBasePtr(); bp = (bp->doSomething())->runtimeCastToBasePtr(); } If this doesn't seem too troublesome, consider that contravarience implies the following would also have to be generated [given some slight changes to usesDoSomething ] void usesDoSomething(Derived* dp) /*as automatically generated by the compiler*/ { dp = (dp->doSomething())->runtimeCastToDerivedPtr(); dp = (dp->doSomething())->runtimeCastToDerivedPtr(); } All these cast functions are virtual, and compilers would have to automatically generate such for all public base classes of a derived class with vptrs, and also would have to implement the dynamic cast of a class to itself. People would have to pay this cost whether they use contravariance or not -- since the compiler cannot determine this when usesDoSomething(Base*) is being compiled. Also, then do compilers automatically generate such virtual functions to also automatically perform downcasting??? If so, this would require a portion of each class's vtable to be generated at link time, as opposed to compile time. These virtual-function downcasts might be implemented by the compiler something like: dp = bp->RuntimeClassInfo()->runtimeCastToDerivedPtr(); where say RuntimeClassInfo() dispatches via "slot 0" of a vtable to a secondary table of "runtime class information" functions that might have to be generated by the compiler at link time. ---- What I'd really like to see is an approach that doesn't cost people anything if they don't do downcasting or contravariance, but would require the compiler to "do the right thing" if such were used. [I can't think of such an approach off the top of my head.] PS: If you don't see the problem for a compiler trying to generate these virtual function runtime casts -- try doing them yourself by hand. See what problems you run into, while [of course] maintaining C++'s traditional modular compilations.