Topic: Pure Virtual Destructors (?)
Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Thu, 2 Dec 1993 01:33:22 GMT Raw View
In article <CHAv8M.79I@pmsr.philips.co.uk> david.binderman@pmsr.philips.co.uk (Dave Binderman) writes:
>
>I wrote:
>: >The answer in the ARM seems confusing and wrong, but that's the standard.
>
>Ronald F. Guilmette (rfg@netcom.com) wrote:
>: David, I think one of us is misinterpreting the ARM. Could you please
>: cite chapter and verse to support your contention in this case?
>
>Page 234, section 10.10c "Virtual Base Classes with Virtual
>Functions":
>
>"A call to a virtual function through one path in an inheritance
>structure may result in the invocation of a function redefined on
>another path"
Ugh! You're right. However counter-intutive it may seem, that's the rule.
(For the purposes of this discussion, we may overlook the fact that this
rule is only elaborated in a commentary section. X3J16 will not have that
luxury however. They will have to put this rule explicitly into their
working paper if they want to keep it.)
Oh, and by the way, the rule you quoted only says that this *may* happen.
The preceeding examples give us an intutive idea of when it *should*
happen (or *must* happen) but the associated verbage is certainly in-
adequate to tell me when I (as a programmer) can actually *expect* it
to happen.
>The real question is: how is the example in the ARM different
>to the original example ?
>
>The major difference I can see is that the example has the virtual
>function called from a constructor.
>
>Is this likely to modify to virtual function calling mechanism ?
Yes. That is yet a separate problem. Even if X3J16 cleans up the given
rule (by clearly specifing the kinds of inheritance lattices which may
cause this effect to occur) and even if they move the cleaned-up rule
into the text of their working paper(s), there is still the matter of
the (probably exceptional) case where the calls are made during execution
of some constructor.
--
-- Ronald F. Guilmette, Sunnyvale, California -------------------------------
------ domain address: rfg@netcom.com ---------------------------------------
------ uucp address: ...!uunet!netcom.com!rfg -------------------------------
Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Thu, 2 Dec 1993 13:38:36 GMT Raw View
In article <rfgCHDwBn.J86@netcom.com> rfg@netcom.com (Ronald F. Guilmette) writes:
>>
>>"A call to a virtual function through one path in an inheritance
>>structure may result in the invocation of a function redefined on
>>another path"
>
>Ugh! You're right. However counter-intutive it may seem, that's the rule.
This is not counter intuitive, but the very basis of mixin
programming. Its called "sibling call".
>
>(For the purposes of this discussion, we may overlook the fact that this
>rule is only elaborated in a commentary section.
Wrong. Its an elementary consequence of the name dominance
rule, which is in the main text of the ARM.
>X3J16 will not have that luxury however.
Thats true, but I wish you would stop using the ANSI
designation for the committee as a whole. X3J16 has special
priviledges in the committee process, but in the long run
the Standard will be an ISO Standard, and ANSI just another
national body that rubber stamps the ISO Standard as their
own (as does, for example, Standards Australia, if they think
they can make money selling copies of the document)
>They will have to put this rule explicitly into their
>working paper if they want to keep it.)
>
>Oh, and by the way, the rule you quoted only says that this *may* happen.
>The preceeding examples give us an intutive idea of when it *should*
>happen (or *must* happen) but the associated verbage is certainly in-
>adequate to tell me when I (as a programmer) can actually *expect* it
>to happen.
Ron, you really should read the actual words in the ARM.
In 10.1.1 page 205 the term dominance is defined and the ARM
is explicit that there is no ambiguity between two names if
one dominates the other. The wording is not precise but its
fairly clear.
The WP has messed up this most important of definitions.
It will have to be fixed.
>
>Yes. That is yet a separate problem. Even if X3J16 cleans up the given
>rule (by clearly specifing the kinds of inheritance lattices which may
>cause this effect to occur) and even if they move the cleaned-up rule
>into the text of their working paper(s), there is still the matter of
>the (probably exceptional) case where the calls are made during execution
>of some constructor.
The issue here is simply whether a call that resolves
to a pure virtual function (possible during construction and
destruction) requires a diagnostic or not. There is no issue
that such a call is ill formed.
There is a related issue: we all know that during
construction, virtual calls work as if the dynamic type
of the object is that of the currently active constructor
body.
However THIS is not specified properly, and depends
on construction of an appropriate object model.
--
JOHN (MAX) SKALLER, INTERNET:maxtal@suphys.physics.su.oz.au
Maxtal Pty Ltd, CSERVE:10236.1703
6 MacKay St ASHFIELD, Mem: SA IT/9/22,SC22/WG21
NSW 2131, AUSTRALIA
Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Sun, 28 Nov 1993 22:34:14 GMT Raw View
In article <KANZE.93Nov19154555@slsvhdt.us-es.sel.de> kanze@us-es.sel.de (James Kanze) writes:
>In article <rfgCGqCKn.GAE@netcom.com> rfg@netcom.com (Ronald F.
>Guilmette) writes:
>
>|> >Perhaps I should have said that I think that within the constructor
>|> >for B, a pure virtual function in VB, not define in B, will resolve to
>|> >the function in A. Since A has been constructed, VB is the base class
>|> >of A, and there is no reason why it shouldn't work this way. After
>|> >all, there is an A object, and VB *is* a base class of it, so it would
>|> >seem perfectly normal that the virtual function in A gets called.
>
>|> James, I understand why it might *seem* that way, but you are still wrong.
>
>|> As I said before, an instance of B DOES NOT contain an instance of A.
>|> Thus, it would be incorrect (while constructing a B) to ever have a function
>|> which is only defined over in A called.
>
>The more I think about it, the more I think that you are right with
>regards to what *should* happen. But I don't think that the ARM
>states this clearly.
I think I could agree with that statement. ;-)
>And different compilers *do* do different
>things. Try the following code on your compiler:
James, you are correct that different compilers do different things. I
simplified your example somewhat, and ran it against four different
compilers. Here is the simplified version:
---------------------------------------------------------------------------
#include <stdio.h>
struct VB
{
VB() { }
virtual void f() { printf ("VB::f()\n"); }
};
struct A : public virtual VB
{
A() { }
virtual void f() { printf ("A::f()\n"); }
};
struct B : public virtual VB { B() { f(); } };
struct C : public A , public B { C() { } };
int main () { C c; return 0; }
---------------------------------------------------------------------------
Using three out of four of the compilers tested (including the recent 2.5.4
release of the g++ compiler), the generated executable program prints VB::f()
(which I believe is the "correct" answer).
One compiler printed A::f()... but I think that is merely a bug in that
compiler.
I agree however that the "required behavior" (if there is any) in such
cases is probably not altogether well specified in either the ARM or in
current X3J16 drafts.
--
-- Ronald F. Guilmette, Sunnyvale, California -------------------------------
------ domain address: rfg@netcom.com ---------------------------------------
------ uucp address: ...!uunet!netcom.com!rfg -------------------------------
Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Sun, 28 Nov 1993 22:48:57 GMT Raw View
In article <CGuoEA.83n@pmsr.philips.co.uk> david.binderman@pmsr.philips.co.uk (Dave Binderman) writes:
>
>James says :
>: Try the following code on your compiler:
>
><code lightly derived from page 234 of the ARM deleted>
>
>Marc says :
>: Cfront 3.0 gives:
>: VB::VB()
>: A::A()
>: B::B() (entering)
>: A::f() <= calls A::f()
>: B::B() (leaving)
>: C::C() (entering)
>: A::f()
>: C::C() (leaving)
>
>: gcc 2.4.5 gives:
>: VB::VB()
>: A::A()
>: B::B() (entering)
>: VB::f() <= calls VB::f()
>: B::B() (leaving)
>: C::C() (entering)
>: A::f()
>: C::C() (leaving)
>
>Also GNU G++ 2.5.3 gives the same answer as G++ 2.4.5
>
>cfront 3.0.1 gets the same answer as the ARM.
>
>The answer in the ARM seems confusing and wrong, but that's the standard.
David, I think one of us is misinterpreting the ARM. Could you please
cite chapter and verse to support your contention in this case?
--
-- Ronald F. Guilmette, Sunnyvale, California -------------------------------
------ domain address: rfg@netcom.com ---------------------------------------
------ uucp address: ...!uunet!netcom.com!rfg -------------------------------
Author: david.binderman@pmsr.philips.co.uk (Dave Binderman)
Date: Tue, 30 Nov 1993 10:21:12 +0000 Raw View
I wrote:
: ><code lightly derived from page 234 of the ARM deleted>
: >
: >Also GNU G++ 2.5.3 gives the same answer as G++ 2.4.5
: >
: >cfront 3.0.1 gets the same answer as the ARM.
: >
: >The answer in the ARM seems confusing and wrong, but that's the standard.
Ronald F. Guilmette (rfg@netcom.com) wrote:
: David, I think one of us is misinterpreting the ARM. Could you please
: cite chapter and verse to support your contention in this case?
Glad to.
Page 234, section 10.10c "Virtual Base Classes with Virtual
Functions":
"A call to a virtual function through one path in an inheritance
structure may result in the invocation of a function redefined on
another path"
The real question is: how is the example in the ARM different
to the original example ?
The major difference I can see is that the example has the virtual
function called from a constructor.
Is this likely to modify to virtual function calling mechanism ?
Regards
--
David C Binderman BSc MSc Academy Programming Services +44 293 544364
dcb@pmsr.philips.co.uk +44 293 654149
Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Fri, 19 Nov 1993 08:21:58 GMT Raw View
In article <KANZE.93Nov4204748@slsvhdt.us-es.sel.de> kanze@us-es.sel.de (James Kanze) writes:
>In article <rfgCFstzD.8Jx@netcom.com> rfg@netcom.com (Ronald F.
>Guilmette) writes:
>
>|> In article <KANZE.93Oct19203352@slsvhdt.us-es.sel.de> kanze@us-es.sel.de (James Kanze) writes:
>|> >In article <rfgCF1zvD.Ax7@netcom.com> rfg@netcom.com (Ronald F.
>|> >Guilmette) writes:
>
>|> >|> I cannot see how such behavior can be (or will be) condoned by the emerging
>|> >|> C++ standard. As far as *I* am concerned, if I have a program which makes
>|> >|> an explicit call to a non-defined pure-virtual function, I'd like to get
>|> >|> an error at link time, thank you.
>
>|> >...
>|> >The only exception I can think of is in the presence of virtual base
>|> >classes. Given the following hierarchy:
>
>|> > VB
>|> > / \
>|> > A B
>|> > \ /
>|> > C
>
>|> >I believe that within the constructor for B, a pure virtual function
>|> >in VB, not defined in B, should resolve to the function in A...
>
>|> What??? Where did you get *that* idea???
>
>|> An instance of B *DOES NOT* contain an instance of A! Thus, while
>|> constructing a B there is no way in hell I should ever be able to
>|> refer in any way to *anything* (e.g. data members *or* function
>|> members) which are *only* present in A objects. No way. Never.
>|> Period. Not even via virtual shenanigans.
>
>Perhaps I should have said that I think that within the constructor
>for B, a pure virtual function in VB, not define in B, will resolve to
>the function in A. Since A has been constructed, VB is the base class
>of A, and there is no reason why it shouldn't work this way. After
>all, there is an A object, and VB *is* a base class of it, so it would
>seem perfectly normal that the virtual function in A gets called.
James, I understand why it might *seem* that way, but you are still wrong.
As I said before, an instance of B DOES NOT contain an instance of A.
Thus, it would be incorrect (while constructing a B) to ever have a function
which is only defined over in A called.
--
-- Ronald F. Guilmette, Sunnyvale, California -------------------------------
------ domain address: rfg@netcom.com ---------------------------------------
------ uucp address: ...!uunet!netcom.com!rfg -------------------------------
Author: kanze@us-es.sel.de (James Kanze)
Date: 19 Nov 1993 14:45:55 GMT Raw View
In article <rfgCGqCKn.GAE@netcom.com> rfg@netcom.com (Ronald F.
Guilmette) writes:
|> >Perhaps I should have said that I think that within the constructor
|> >for B, a pure virtual function in VB, not define in B, will resolve to
|> >the function in A. Since A has been constructed, VB is the base class
|> >of A, and there is no reason why it shouldn't work this way. After
|> >all, there is an A object, and VB *is* a base class of it, so it would
|> >seem perfectly normal that the virtual function in A gets called.
|> James, I understand why it might *seem* that way, but you are still wrong.
|> As I said before, an instance of B DOES NOT contain an instance of A.
|> Thus, it would be incorrect (while constructing a B) to ever have a function
|> which is only defined over in A called.
The more I think about it, the more I think that you are right with
regards to what *should* happen. But I don't think that the ARM
states this clearly. And different compilers *do* do different
things. Try the following code on your compiler:
------------------------------------------------------
#include <iostream.h>
class VB
{
public :
VB() { cout << "VB::VB()" << endl ; }
virtual void f() { cout << "VB::f()" << endl ; }
} ;
class A : public virtual VB
{
public :
A() { cout << "A::A()" << endl ; }
virtual void f() { cout << "A::f()" << endl ; }
} ;
class B : public virtual VB
{
public :
B()
{
cout << "B::B() (entering)" << endl ;
f() ;
cout << "B::B() (leaving)" << endl ;
}
} ;
class C : public A , public B
{
public :
C()
{
cout << "C::C() (entering)" << endl ;
f() ;
cout << "C::C() (leaving)" << endl ;
}
} ;
int
main()
{
C c ;
return 0 ;
}
------------------------------------------------------
Cfront and g++ do *not* do the same thing.
--
James Kanze email: kanze@us-es.sel.de
GABI Software, Sarl., 8 rue du Faisan, F-67000 Strasbourg, France
Conseils en informatique industrielle --
-- Beratung in industrieller Datenverarbeitung
Author: marc@offline.be (Marc Duponcheel)
Date: 20 Nov 93 00:31:28 GMT Raw View
In article <KANZE.93Nov19154555@slsvhdt.us-es.sel.de> kanze@us-es.sel.de (James Kanze) writes:
> The more I think about it, the more I think that you are right with
> regards to what *should* happen. But I don't think that the ARM
> states this clearly. And different compilers *do* do different
> things. Try the following code on your compiler:
>
> ------------------------------------------------------
> #include <iostream.h>
>
> class VB
> {
> public :
> VB() { cout << "VB::VB()" << endl ; }
> virtual void f() { cout << "VB::f()" << endl ; }
>
> } ;
>
> class A : public virtual VB
> {
> public :
> A() { cout << "A::A()" << endl ; }
> virtual void f() { cout << "A::f()" << endl ; }
> } ;
>
> class B : public virtual VB
> {
> public :
> B()
> {
> cout << "B::B() (entering)" << endl ;
> f() ;
> cout << "B::B() (leaving)" << endl ;
> }
>
> } ;
>
> class C : public A , public B
> {
> public :
> C()
> {
> cout << "C::C() (entering)" << endl ;
> f() ;
> cout << "C::C() (leaving)" << endl ;
> }
> } ;
>
> int
> main()
> {
> C c ;
> return 0 ;
> }
> ------------------------------------------------------
Cfront 3.0 gives:
VB::VB()
A::A()
B::B() (entering)
A::f() <= calls A::f()
B::B() (leaving)
C::C() (entering)
A::f()
C::C() (leaving)
gcc 2.4.5 gives:
VB::VB()
A::A()
B::B() (entering)
VB::f() <= calls VB::f()
B::B() (leaving)
C::C() (entering)
A::f()
C::C() (leaving)
First of all, since B calls a virtual function f in its ctor, the results
is (if I remeber well stated officially) calling f as a not virtual
function (the vtbl is not ready yet). In many cases this may give compiler
dependent results so it is not good to do so.
It looks to me that Cfront is 'wrong' since looking at the dependency B isa
VB alone it is clear that VB::f() should be called (again compiler
dependency IS legal).
Of course, if you add:
virtual void g() { f(); }
to VB,A and B
and
virtual void g() { f(); }
to C
then gcc and Cfront agree if you call c.g() AFTER c has been constructed.
I follow one rule: don't rely on virtual behaviour at ctor and dtor time.
At work (BCC) we sometimes got 'pure virtual called' problems at dtor time
caused to this fact and I can only blame the programmer, not the compiler.
-- marc.
################################################################################
email preferred address marc@offline.be [= me@home]
aka marc@offline.UUCP ub4b!offline!marc offline!marc
if [me@home] fails mdu@abacus.be [= me@brussels.work]
or mdp@cimad.be [= me@antwerp.work]
fido 2:292/603.26 Marc.Duponcheel@p26.f603.n292.z2.FidoNet.Org [= me@home]
bix mduponcheel mduponcheel@BIX.com [= me@home]
################################################################################
Author: david.binderman@pmsr.philips.co.uk (Dave Binderman)
Date: Sun, 21 Nov 1993 16:31:07 +0000 Raw View
James says :
: Try the following code on your compiler:
<code lightly derived from page 234 of the ARM deleted>
Marc says :
: Cfront 3.0 gives:
: VB::VB()
: A::A()
: B::B() (entering)
: A::f() <= calls A::f()
: B::B() (leaving)
: C::C() (entering)
: A::f()
: C::C() (leaving)
: gcc 2.4.5 gives:
: VB::VB()
: A::A()
: B::B() (entering)
: VB::f() <= calls VB::f()
: B::B() (leaving)
: C::C() (entering)
: A::f()
: C::C() (leaving)
Also GNU G++ 2.5.3 gives the same answer as G++ 2.4.5
cfront 3.0.1 gets the same answer as the ARM.
The answer in the ARM seems confusing and wrong, but that's the standard.
What G++ does seems more intuitive.
Marc writes:
: I follow one rule: don't rely on virtual behaviour at ctor and dtor time.
I also recommend not using virtual base classes.
Regards
--
David C Binderman BSc MSc Academy Programming Services +44 293 544364
dcb@pmsr.philips.co.uk +44 293 654149
5 years C++ / 8 years C / 12 years UNIX / telecomms / compilers / real time
Author: kanze@us-es.sel.de (James Kanze)
Date: 4 Nov 93 20:47:48 Raw View
In article <rfgCFstzD.8Jx@netcom.com> rfg@netcom.com (Ronald F.
Guilmette) writes:
|> In article <KANZE.93Oct19203352@slsvhdt.us-es.sel.de> kanze@us-es.sel.de (James Kanze) writes:
|> >In article <rfgCF1zvD.Ax7@netcom.com> rfg@netcom.com (Ronald F.
|> >Guilmette) writes:
|> >|> I cannot see how such behavior can be (or will be) condoned by the emerging
|> >|> C++ standard. As far as *I* am concerned, if I have a program which makes
|> >|> an explicit call to a non-defined pure-virtual function, I'd like to get
|> >|> an error at link time, thank you.
|> >...
|> >The only exception I can think of is in the presence of virtual base
|> >classes. Given the following hierarchy:
|> > VB
|> > / \
|> > A B
|> > \ /
|> > C
|> >I believe that within the constructor for B, a pure virtual function
|> >in VB, not defined in B, should resolve to the function in A...
|> What??? Where did you get *that* idea???
|> An instance of B *DOES NOT* contain an instance of A! Thus, while
|> constructing a B there is no way in hell I should ever be able to
|> refer in any way to *anything* (e.g. data members *or* function
|> members) which are *only* present in A objects. No way. Never.
|> Period. Not even via virtual shenanigans.
Perhaps I should have said that I think that within the constructor
for B, a pure virtual function in VB, not define in B, will resolve to
the function in A. Since A has been constructed, VB is the base class
of A, and there is no reason why it shouldn't work this way. After
all, there is an A object, and VB *is* a base class of it, so it would
seem perfectly normal that the virtual function in A gets called.
--
James Kanze email: kanze@us-es.sel.de
GABI Software, Sarl., 8 rue du Faisan, F-67000 Strasbourg, France
Conseils en informatique industrielle --
-- Beratung in industrieller Datenverarbeitung
Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Mon, 1 Nov 1993 05:52:49 GMT Raw View
In article <KANZE.93Oct19203352@slsvhdt.us-es.sel.de> kanze@us-es.sel.de (James Kanze) writes:
>In article <rfgCF1zvD.Ax7@netcom.com> rfg@netcom.com (Ronald F.
>Guilmette) writes:
>
>|> I cannot see how such behavior can be (or will be) condoned by the emerging
>|> C++ standard. As far as *I* am concerned, if I have a program which makes
>|> an explicit call to a non-defined pure-virtual function, I'd like to get
>|> an error at link time, thank you.
>
>Come to think of it, you are right.
You sound surprized! :-) :-)
>A call to a pure virtual function (defined or otherwise) cann only
>occur when the explicite scope operator is used, or in a constructor
>or a destructor. (Any other occurance would imply that you had
>instantiated an class.)
>
>In both cases, the compiler knows the class where to start looking,
>and knows the entire hierarchy up until that point. So there is no
>reason for it to use the virtual calling mechanism at all. And
>calling a function directly, rather than via the virtual mechanism,
>should result in a link time error.
One would hope so anyway... but as I pointed out in another message, I'm
not aware of any rule in the ARM or in the X3J16 drafts which says that
a called function must be defined somewhere in the program (presumably,
no more than once).
P.S. The ANSI C standard already *does* have such a rule... but who looks
at that? :-)
--
-- Ronald F. Guilmette, Sunnyvale, California -------------------------------
------ domain address: rfg@netcom.com ---------------------------------------
------ uucp address: ...!uunet!netcom.com!rfg -------------------------------
Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Mon, 1 Nov 1993 05:59:36 GMT Raw View
In article <KANZE.93Oct19203352@slsvhdt.us-es.sel.de> kanze@us-es.sel.de (James Kanze) writes:
>In article <rfgCF1zvD.Ax7@netcom.com> rfg@netcom.com (Ronald F.
>Guilmette) writes:
>
>|> I cannot see how such behavior can be (or will be) condoned by the emerging
>|> C++ standard. As far as *I* am concerned, if I have a program which makes
>|> an explicit call to a non-defined pure-virtual function, I'd like to get
>|> an error at link time, thank you.
>
>...
>The only exception I can think of is in the presence of virtual base
>classes. Given the following hierarchy:
>
> VB
> / \
> A B
> \ /
> C
>
>I believe that within the constructor for B, a pure virtual function
>in VB, not defined in B, should resolve to the function in A...
What??? Where did you get *that* idea???
An instance of B *DOES NOT* contain an instance of A! Thus, while
constructing a B there is no way in hell I should ever be able to
refer in any way to *anything* (e.g. data members *or* function
members) which are *only* present in A objects. No way. Never.
Period. Not even via virtual shenanigans.
--
-- Ronald F. Guilmette, Sunnyvale, California -------------------------------
------ domain address: rfg@netcom.com ---------------------------------------
------ uucp address: ...!uunet!netcom.com!rfg -------------------------------
Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Sun, 17 Oct 1993 18:11:37 GMT Raw View
In article <KANZE.93Oct7192246@slsvhdt.us-es.sel.de> kanze@us-es.sel.de (James Kanze) writes:
>By the way, at least one compiler vendor (GNU, although perhaps vendor
>isn't the correct word) generates a body for a pure virtual function
>if you don't provide one, precisely to catch calls like the above.
>(The body just calls 'abort.' An error message would have been more
>helpful, but its still a better than just any old undefined behavior.)
I cannot see how such behavior can be (or will be) condoned by the emerging
C++ standard. As far as *I* am concerned, if I have a program which makes
an explicit call to a non-defined pure-virtual function, I'd like to get
an error at link time, thank you.
(Follow-ups to comp.std.c++ please.)
--
-- Ronald F. Guilmette ------------------------------------------------------
------ domain address: rfg@netcom.com ---------------------------------------
------ uucp address: ...!uunet!netcom.com!rfg -------------------------------