Hello. Developing very large program I met a problem with covariant return types, which I tried to bypass with void return types, but did not achieve success due to a bug in g++. Fortunately, I managed to reproduce the most of the effects in very little example, which I am sending you. But please be aware that similar problems perhaps appear at different class configurations, not only at this one. (I know because in my real program configuration is little bit different). I have tested this example at two different sites with two compilers (both - PC Pentium 4 with RedHat): First: Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/3.2/specs Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --disable-checking --host=i386-redhat-linux --with-system-zlib --enable-__cxa_atexit Thread model: posix gcc version 3.2 20020903 (Red Hat Linux 8.0 3.2-7) Second: Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/2.96/specs gcc version 2.96 20000731 (Red Hat Linux 7.3 2.96-113) The output of the both cases is the case: SuperDerived::print: Hello SuperDerived::fun: Hello SuperDerived::fun: Hello SuperDerived::fun: Hello Here only the first line is correct. Three the others are not. In all the cases one should expect the reply from function print() like occured in the first line. But the type information is somehow broken during the static casts, which are expected to affect the type assigned by compiler to the pointer, but not the type of the object, by my understanding. This violation results in involving different function of the class. In my real program this results in skipping the print function at all and in segmentation fault a few lines later. Some comments on how static casts are extected to function can be obtained in B. Stroustrup, The C++ Programming Language, Special edition, Addison-Weley, p. 413. I would appreciate if you let me know whether this problem has already been corrected in newer release of g++, or not, or can it be corrected at all. Are there ways to bypass it. By my opinition this is a real and very serious problem. Thank you. Regards Igor Smirnov The code: #include <iostream.h> class Base1 {public: int j; virtual void print(void) { cout<<"Base1::print: Hello\n";} }; class Base2 {public: int k; virtual void fun(void) = 0; virtual void print(void) { cout<<"Base2::print: Hello\n";} }; class Derived: public Base1 {public: int l; virtual void print(void) { cout<<"Derived::print: Hello\n";} }; class SuperDerived: public Base2, public Derived {public: int m; virtual void fun(void) { cout<<"SuperDerived::fun: Hello\n";} virtual void print(void) { cout<<"SuperDerived::print: Hello\n";} }; int main(void) { SuperDerived sd; Derived* ad = new SuperDerived(sd); ad->print(); ad = ( Derived* ) ( (void*) new SuperDerived(sd) ); ad->print(); ad = static_cast< Derived* > ( (void*) new SuperDerived(sd) ); ad->print(); ad = static_cast< Derived* > ( static_cast< void* > ( new SuperDerived(sd) ) ); ad->print(); }
I think this is undefined behavior but I do not know. A work around for this testcase is to swap the order of the base classes of the super most class: class SuperDerived: public Derived, public Base2
I can confirm this with all compiler up to and including mainline. And also with Intel's icc7. However, your code is invalid. When you cast to void*, the compiler loses track of where the Derived part of your SuperDerived object resides. Then, when you cast back from void*, the compiler has to assume that this cast does not need any pointer adjustment. There is simply no way the compiler can do what you want in view of this. You can't cast to void* and expect the compiler to work with this correctly. W.
Yes. In legalese, the standard says that if you cast a pointer to an object to a pointer to void, the only valid thing you can do with that is casting it back to the same object type and use it. Otherwise, you hit undefined behaviour.
Hello, I am quiote susprised by this letter. May I ask a few questions. (In reply to comment #2) > I can confirm this with all compiler up to and including mainline. And > also with Intel's icc7. > > However, your code is invalid. When you cast to void*, the compiler > loses track of where the Derived part of your SuperDerived object > resides. What does it mean? Why the Derived part should be located somewhere else? I thought and think that this is correct that additions of SuperDerived just follow components of Derived. > Then, when you cast back from void*, the compiler has to > assume that this cast does not need any pointer adjustment. Again, this is not clear, but this correlates with the first question. I thought (may be naively) that the convertion of pointer should affect neither of binary representation of object, nor the pointer itself. > There is > simply no way the compiler can do what you want in view of this. You > can't cast to void* and expect the compiler to work with this correctly. By my understanding it can be done trivially, so surely there is the way. Just not touch referred object, and that's all. Let me reprint here citation from the mentioned book of Stroustrup (B.Stroustrup, The C++ Programming Language, Special edition, Addison-Weley, p. 413): "The compiler cannot assume anything about the memory pointed to by a void*. This implies that dynamic_cast - which must look into an object to determine its type - cannot cast from a void*. For that, a static_cast is needed. For example: Radio* f(void* p) { Storable* ps = static_cast<Storable*>(p); // trust the programmer return dynamic_cast<Radio*>(ps); } " (finish of citation) And looking at the previous example just at the same page in this boook we can read the comment: "Storable is virtual base of Radio". So, it looks like that conversion of pointer to void* (may be implicit at calling this function, or explicit, but this should not make differencce, do you agree?) by the opinion of the creator of this language should not destroy the object and its type and should allow later correct static casts of pointer to the base type. This is valid even for virtual base class. Could you comment this, please? Regards Igor Smirnov
(In reply to comment #4) Hello again, Let me annul this my connent. I understood what you mean. When you implement covariant return types? Please do this quicklier. Igor > Hello, > > I am quiote susprised by this letter. May I ask a few questions. > > (In reply to comment #2) > > I can confirm this with all compiler up to and including mainline. And > > also with Intel's icc7. > > > > However, your code is invalid. When you cast to void*, the compiler > > loses track of where the Derived part of your SuperDerived object > > resides. > > What does it mean? Why the Derived part should be located somewhere else? > I thought and think that this is correct that additions of SuperDerived > just follow components of Derived. > > > Then, when you cast back from void*, the compiler has to > > assume that this cast does not need any pointer adjustment. > > Again, this is not clear, but this correlates with the first question. > I thought (may be naively) that the convertion of pointer should affect > neither of binary representation of object, nor the pointer itself. > > > > There is > > simply no way the compiler can do what you want in view of this. You > > can't cast to void* and expect the compiler to work with this correctly. > > By my understanding it can be done trivially, so surely there is the way. Just > not touch referred object, and that's all. > > Let me reprint here citation from the mentioned book of Stroustrup > (B.Stroustrup, The C++ Programming Language, Special edition, Addison-Weley, p. > 413): > > "The compiler cannot assume anything about the memory pointed to by a void*. > This implies that dynamic_cast - which must look into an object to determine its > type - cannot cast from a void*. For that, a static_cast is needed. For example: > > Radio* f(void* p) > { > Storable* ps = static_cast<Storable*>(p); // trust the programmer > return dynamic_cast<Radio*>(ps); > } > " (finish of citation) > > And looking at the previous example just at the same page in this boook we can > read the comment: "Storable is virtual base of Radio". So, it looks like that > conversion of pointer to void* (may be implicit at calling this function, or > explicit, but this should not make differencce, do you agree?) by the opinion of > the creator of this language should not destroy the object and its type and > should allow later correct static casts of pointer to the base type. This is > valid even for virtual base class. Could you comment this, please? > > Regards > Igor Smirnov > > >
Covariant return types are implemented in the upcoming 3.4. However, they have nothing to do with your problem. Really, you have to understand that a cast can change the representation of a pointer. One example is when you have a pointer to a derived class which has two base classes. Casting to one of them must change the pointer, since the two base classes cannot be at the same memory location. This is also the reason for the thing Giovanni quotet: when you cast to void*, the _only_ thing that you can do with this pointer is to cast it back to the _exact same_ type. You did not do that, so that's where your error lies. Please read up on this somewhere. W.
> "The compiler cannot assume anything about the memory pointed to by a void*. > This implies that dynamic_cast - which must look into an object to determine its > type - cannot cast from a void*. For that, a static_cast is needed. For example: > Radio* f(void* p) > { > Storable* ps = static_cast<Storable*>(p); // trust the programmer > return dynamic_cast<Radio*>(ps); > } > " (finish of citation) > And looking at the previous example just at the same page in this boook we can > read the comment: "Storable is virtual base of Radio". So, it looks like that > conversion of pointer to void* (may be implicit at calling this function, or > explicit, but this should not make differencce, do you agree?) by the opinion of > the creator of this language should not destroy the object and its type and > should allow later correct static casts of pointer to the base type. This is > valid even for virtual base class. Could you comment this, please? You're still wrong, and you are trying to make your book say something it does not. If you look carefully at the example you posted from the book, you will see that the cast chain is like this: Storable* -> void* -> Storable* -> Radio*. In your code, you do like this: SuperDerived* -> void* -> Derived*. You cannot do this. The book explains you why: "The compiler cannot assume anything about the memory pointed to by a void*.". The compiler will have to trust the programmer (as the comment in the example says) and blindly convert the pointer back to its *ORIGINAL* type (the type it had before it was casted to void*). Whether you understand the technical problem or not is insignificant. Both the standard and C++PL explain you that if you have a void* the only sensible thing you can do is casting it back to its original type. If you got a void* from a SuperDerived*, you cannot cast it to Derived*. This is wrong: the standard says it is undefined behaviour, and you get it. You MUST cast it back to a SuperDerived before doing anything else. This would work: static_cast<Derived*> (static_cast<SuperDerived*> (static_cast<void*> (new SuperDerived()))). Also, if you cast it to a Derived* BEFORE casting to void*, you then must cast it to Derived* again. For instance: SuperDerived* -> Derived* -> void* -> SuperDerived* = WRONG SuperDerived* -> Derived* -> void* -> Derived* -> SuperDerived* = CORRECT
Subject: Re: Run time error, breaking of type info and static casts On 5 Apr 2004, giovannibajo at libero dot it wrote: > > ------- Additional Comments From giovannibajo at libero dot it 2004-04-05 15:36 ------- > > "The compiler cannot assume anything about the memory pointed to by a void*. > > This implies that dynamic_cast - which must look into an object to determine > its > > type - cannot cast from a void*. For that, a static_cast is needed. For > example: > > Radio* f(void* p) > > { > > Storable* ps = static_cast<Storable*>(p); // trust the programmer > > return dynamic_cast<Radio*>(ps); > > } > > " (finish of citation) > > > And looking at the previous example just at the same page in this boook we can > > read the comment: "Storable is virtual base of Radio". So, it looks like that > > conversion of pointer to void* (may be implicit at calling this function, or > > explicit, but this should not make differencce, do you agree?) by the opinion > of > > the creator of this language should not destroy the object and its type and > > should allow later correct static casts of pointer to the base type. This is > > valid even for virtual base class. Could you comment this, please? > > You're still wrong, and you are trying to make your book say something it does > not. If you look carefully at the example you posted from the book, you will > see that the cast chain is like this: Storable* -> void* -> Storable* -> > Radio*. No, there is no such cast chain defined in the book. I looked carefully. In fact there is no any explicit statement about the way of convertion of the pointer Radio* into void*, which is assumed before the call of Radio* f(void* p). So it might be with intermediate Storable*, and equally might be direct Radio* -> void*. > The book explains you why: "The compiler cannot assume anything about the > memory pointed to by a void*.". The compiler will have to trust the programmer > (as the comment in the example says) and blindly convert the pointer back to > its *ORIGINAL* type (the type it had before it was casted to void*). The citation is correct. But your comment is not. The book does not say this. > > Whether you understand the technical problem or not is insignificant. Both the > standard and C++PL explain you that if you have a void* the only sensible thing > you can do is casting it back to its original type. Here I have different approach and estimate of significant things, but I don't understand, what you mean by "C++PL"? The book of Stroustrup? But I have not seen this in it. May be I missed it, it is too big.
Subject: Re: Run time error, breaking of type info and static casts Igor, I am sure Stroustrup explains it somewhere, maybe with different wording. I'll cite you the ISO/ANSI C++ standard ([expr.static.cast]/4): "Any expression can be explicitly converted to type “cv void.” The expression value is discarded." When it says that the value is discarded, it means that the expression does not yield a meaningful value anymore. Later, in paragraph 10, it says: " An rvalue of type “pointer to cv void” can be explicitly converted to a pointer to object type. A value of type pointer to object converted to “pointer to cv void” and back to the original pointer type will have its original value.". As you can see, the only way to get back the original value is to convert it back to the original pointer type. I hope this is clear enough. Giovanni Bajo