Topic: A puzzle involving object destruction (try on YOUR compiler!)


Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Mon, 7 Nov 1994 04:24:58 GMT
Raw View
In article <CyHK7s.6Cu@borland.com> pete@genghis.interbase.borland.com (Pete Becker) writes:
> When interpreting a document such as the ARM you must be sure to read
>all of the relevant parts.

 This is very true and one of the problems which a Standard
should solve. What the Standard says must be true _without exception_
and without any need to look all through the whole of the document
for some other rule that we all "know" overrides what the first
rule says. That will tend to make the Standard somewhat more
verbose than the ARM: the Standard has to say exactly what it means.
--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,
        81A Glebe Point Rd, GLEBE   Mem: SA IT/9/22,SC22/WG21
        NSW 2037, AUSTRALIA     Phone: 61-2-566-2189




Author: fjh@mundil.cs.mu.OZ.AU (Fergus Henderson)
Date: Wed, 26 Oct 1994 18:50:42 GMT
Raw View
rfg@netcom.com (Ronald F. Guilmette) writes:

>I tried three different compilers (all on UNIX) and got three different
>set of results, i.e.:
>
> Compiler 1 Compiler 2 Compiler 3
> --------------------------------------------
>   ~T2    ~B    ~B
>   ~T1    ~A    ~A
>   ~B    ~T1    ~T2
>   ~A    ~T2    ~T1
>
>Which compiler is right?

I think they're ALL wrong.  I think it should be

 ~T2
 ~B
 ~T1
 ~A

This is because "destruction [of initialized static objects] is done in
reverse order of construction", and the order of construction must be

 A
 T1
 B
 T2

>Note that sections 6.7 (last paragraph) and also 3.5.3 (first paragraph)
>of the current working paper seem to be the primary (and only?) references
>available to answer this question, but even they are not terribly clear.
>(In particular, the final sentence of the first paragraph of 3.5.3 is
>_really_ open to interpretation.)

Yes.  WP 6.7 and 3.5.3 seem to be the same as ARM 6.7 and the end of ARM 3.4.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: hopps@mmm.com (Kevin J Hopps)
Date: 25 Oct 1994 13:47:50 GMT
Raw View
Ronald F. Guilmette (rfg@netcom.com) wrote:
> Here is a simple puzzle involving object destruction order.  What must
> the following program print (when compiled with a C++ compiler which
> claims to conform to the latest X3J16/WG21 draft)?

[destruction order code removed]

> What do other compilers do?

SunSoft C++ 4.0 prints
    ~B
    ~A
    ~T1
    ~T2

> The above program is so simple that I cannot see any compelling reason
> why the final C++ standard should not absolutely mandate one (and only
> one) ``correct'' result for this code.  If it fails to do that, I think
> it will be indicative of a triumph of the implementors at the expense
> of the portability of the ordinary user's C++ code.

I agree that the behavior should be well defined.  But to characterize
the lack of definition as indicative of a triumph of the implementors
is puzzling.  What is it that implementors would win?
--
Kevin J. Hopps                  e-mail: kjhopps@mmm.com
3M Company                      phone:  (612) 737-3300
3M Center, Bldg. 235-2D-45      fax:    (612) 737-2700
St. Paul, MN 55144-1000         Opinions are my own.  I don't speak for 3M.




Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Thu, 27 Oct 1994 07:22:22 GMT
Raw View
In article <1994Oct25.122935.11942@cae.ca> balasa@cae.ca (Alexandru Balasa) writes:
>Here is the 4-th result, from DEC cxx on OSF/1 1.3:
>~B
>~T2
>~A
>~T1
>This makes sense to me; the inner object is destroyed after the outer
>object ( otherwise, the B destructor would not have a whole object )
>and B and A objects are destroyed in the reverse order; does it look
>weird ?

Actually, I think that is the only sensible order.

The rules of the language as they now stand effectively force implementors
to register the need for a later destruction only AT THE END OF (and upon
a `successful' completion of) a constructor for the relevant object.  The
reason I say this is that the current draft now requires that a destructor
NOT be called unless the corresponding constructor for the object in
question has been completed normally (i.e. via a return statement or
by falling off the end).  So if you are an implementor, and if you
use a simple `destructor registration' model of implementation (at least
mentally, if not actually and physically in your implementation) then
given a constructor like:

 A::A () { static T1 t1; }
 ...
 A a;

you will actually _finish_ the construction of `t1' before you finish the
construction of `a', so the destructor for `t1' will be (in effect)
`registered' (or pushed onto the to-do stack) _before_ the destructor
for `a'.  But later on, at program exit time, you will process that
pending `to-do' list of pending destructions in LIFO order, so the
result should be:

 ~A
 ~T1

Now I just hope that the committee will Do The Right Thing by mandating
this precise order, and that implementors will get on board and all
agree to do this one (and only one) way.

--

-- Ron Guilmette, Sunnyvale, CA ---------- RG Consulting -------------------
---- E-mail: rfg@rahul.net ----------------- Purveyors of Compiler Test ----
-------------------------------------------- Suites and Bullet-Proof Shoes -




Author: pete@genghis.interbase.borland.com (Pete Becker)
Date: Fri, 28 Oct 1994 12:47:38 GMT
Raw View
In article <rfgCyDHCy.1xx@netcom.com>,
Ronald F. Guilmette <rfg@netcom.com> wrote:
>In article <38j2a6$mdc@dawn.mmm.com> kjhopps@mmm.com writes:
>>Ronald F. Guilmette (rfg@netcom.com) wrote:
>>> The above program is so simple that I cannot see any compelling reason
>>> why the final C++ standard should not absolutely mandate one (and only
>>> one) ``correct'' result for this code.  If it fails to do that, I think
>>> it will be indicative of a triumph of the implementors at the expense
>>> of the portability of the ordinary user's C++ code.
>>
>>I agree that the behavior should be well defined.  But to characterize
>>the lack of definition as indicative of a triumph of the implementors
>>is puzzling.  What is it that implementors would win?
>
>The right NOT to have to rework their implementations substantially.
>

 Since this has not yet been proposed or even discussed informally in
any of the committee meetings that I have attended, it is nothing more than
speculation to contend that implementors would oppose it, or that if it failed
that that failure would be solely due to implementor opposition.
 Personally, I would much prefer to see proposals like this recommended
on the basis of their technical merits rather than paranoid ad hominem
arguments such as this one.
 -- Pete




Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Sat, 29 Oct 1994 08:25:22 GMT
Raw View
In article <CyDvJE.1Dw@borland.com> pete@genghis.interbase.borland.com (Pete Becker) writes:
>In article <rfgCyDHCy.1xx@netcom.com>,
>Ronald F. Guilmette <rfg@netcom.com> wrote:
>>In article <38j2a6$mdc@dawn.mmm.com> kjhopps@mmm.com writes:
>>>Ronald F. Guilmette (rfg@netcom.com) wrote:
>>>> The above program is so simple that I cannot see any compelling reason
>>>> why the final C++ standard should not absolutely mandate one (and only
>>>> one) ``correct'' result for this code.  If it fails to do that, I think
>>>> it will be indicative of a triumph of the implementors at the expense
>>>> of the portability of the ordinary user's C++ code.
>>>
>>>I agree that the behavior should be well defined.  But to characterize
>>>the lack of definition as indicative of a triumph of the implementors
>>>is puzzling.  What is it that implementors would win?
>>
>>The right NOT to have to rework their implementations substantially.
>>
>
> Since this has not yet been proposed or even discussed informally in
>any of the committee meetings that I have attended, it is nothing more than
>speculation to contend that implementors would oppose it, or that if it failed
>that that failure would be solely due to implementor opposition.
> Personally, I would much prefer to see proposals like this recommended
>on the basis of their technical merits rather than paranoid ad hominem
>arguments such as this one.

Pete, you are absolutely correct, and I withdraw my earlier comments.

As regards to recommending that all `conformant' compilers should yield the
same result for my simple example, I must confess that I have no specific
argument which would look like it was based upon `technical merit' to offer
in support of such an idea.  All I can say is that it is clearly possible
for all compilers to produce the same results, and that I cannot think of
any argument which would really justify compiler-to-compiler deviations in
this instance.

--

-- Ron Guilmette, Sunnyvale, CA ---------- RG Consulting -------------------
---- E-mail: rfg@rahul.net ----------------- Purveyors of Compiler Test ----
-------------------------------------------- Suites and Bullet-Proof Shoes -




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Sun, 30 Oct 1994 00:22:43 GMT
Raw View
John H. Spicer wrote (in email, which I'm posting on his behalf since
his news posting software isn't working):
>
> I agree with your reasoning, and almost with your conclusions.
>
> The exact WP wording from 3.5.3 is "Destruction is done in reverse order
> of initialization".  The question is, when is something initialized.
> I believe that the consensus on the committee is that an object is
> initialized when its constructor completes.

Yes - you're right.

> Consequently, the first object initialized
> is T1, then A, then T2, then B.  So, the result should be
>
> ~B
> ~T2
> ~A
> ~T1

Yes.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: chase@centerline.com (David Chase)
Date: 31 Oct 1994 17:15:53 GMT
Raw View
pete@genghis.interbase.borland.com (Pete Becker) writes:

>  The problem I see with it is that the rule
> becomes rather complicated to apply in non-trivial
> cases. This means that users may not understand it,
> and therefore won't rely on it. In which case it's
> pointless.

I disagree.  Users may not understand it, but they
may very well rely on it accidentally.  Then, upon
changing hardware, OS, compiler vendor, or compiler
version, they may be unpleasantly surprised.  The
fewer parts of the language are left gratuitously
undefined, the more useful (predictable, portable,
reliable) the language will be.

David Chase (speaking for myself)
CenterLine Software




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Mon, 31 Oct 1994 19:53:14 GMT
Raw View
Posted on behalf of jhs@edg.com (John H. Spicer):

> In article <9430311.28588@mulga.cs.mu.oz.au>,
> Fergus Henderson <fjh@munta.cs.mu.OZ.AU> wrote:
> >John H. Spicer wrote (in email, which I'm posting on his behalf since
> >his news posting software isn't working):
> >>
> >> I agree with your reasoning, and almost with your conclusions.
> >>
> >> The exact WP wording from 3.5.3 is "Destruction is done in reverse order
> >> of initialization".  The question is, when is something initialized.
> >> I believe that the consensus on the committee is that an object is
> >> initialized when its constructor completes.
> >
> >Yes - you're right.
> >
> >> Consequently, the first object initialized
> >> is T1, then A, then T2, then B.  So, the result should be
> >>
> >> ~B
> >> ~T2
> >> ~A
> >> ~T1
> >
> >Yes.
> >
>
>  No.
>  When interpreting a document such as the ARM you must be sure to read
> all of the relevant parts. In particular, section 6.5 answers this question
> explicitly:
>
>  The destructor for a local object with static storage duration
>  will be executed if and only if the variable was constructed.
>  The destructor must be called either immediately before or as
>  part of the calls of the atexit() functions (3.5). EXACTLY WHEN
>  IS UNSPECIFIED. [emphasis added]
>
>  -- Pete

I'm aware of the wording in 6.7 (of 9/20 WP).  It is irrelevent to the
order of destructor calls.  It simply says that the destruction of local
objects must be done before or as part of the atexit processing.

Actually, if you look carefully at the WP you realize that 6.7 must
be obsolete, because 3.5.3 says that objects initialized before an atexit()
call may not be destroyed until after the function specified in the
atexit() call has been called.   Note that 3.5.3 makes no distinction
between local statics and global statics.  From reading both 3.5.3 and 6.7
one can conclude that the destruction of local statics and global statics
must both be done in the overall reverse order of construction.  This is the
only way to prevent violating the rule regarding the interaction of
destructions and atexit processing.

The significance of 6.7 is that you can't know precisely when, in
relationship to the calls made as part of atexit processing, when
a given static destruction will be done.  This part of 6.7 should be
merged with 3.5.3 as it causes confusion the way it is now.

John.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Sat, 22 Oct 1994 09:38:13 GMT
Raw View
Here is a simple puzzle involving object destruction order.  What must
the following program print (when compiled with a C++ compiler which
claims to conform to the latest X3J16/WG21 draft)?

--------------------------------------------------------------------------
#include <stdio.h>

struct T1 { ~T1 () { printf ("~T1\n"); } };
struct T2 { ~T2 () { printf ("~T2\n"); } };

struct A
{
    A () { static T1 T1_local; }
    ~A () { printf ("~A\n"); }
};

struct B
{
    B () { static T2 T2_local; }
    ~B () { printf ("~B\n"); }
};

A a;
B b;

int main () { return 0; }
--------------------------------------------------------------------------

Try to guess the Right Answer before you try the code on _your_ compiler
(which I strongly recommend) AND before you read the rest of this posting.



I tried three different compilers (all on UNIX) and got three different
set of results, i.e.:

 Compiler 1 Compiler 2 Compiler 3
 --------------------------------------------
   ~T2    ~B    ~B
   ~T1    ~A    ~A
   ~B    ~T1    ~T2
   ~A    ~T2    ~T1

Which compiler is right?

Note that sections 6.7 (last paragraph) and also 3.5.3 (first paragraph)
of the current working paper seem to be the primary (and only?) references
available to answer this question, but even they are not terribly clear.
(In particular, the final sentence of the first paragraph of 3.5.3 is
_really_ open to interpretation.)

What do other compilers do?

The above program is so simple that I cannot see any compelling reason
why the final C++ standard should not absolutely mandate one (and only
one) ``correct'' result for this code.  If it fails to do that, I think
it will be indicative of a triumph of the implementors at the expense
of the portability of the ordinary user's C++ code.

--

-- Ron Guilmette, Sunnyvale, CA ---------- RG Consulting -------------------
---- domain addr: rfg@netcom.com ----------- Purveyors of Compiler Test ----
---- uucp addr: ...!uunet!netcom!rfg ------- Suites and Bullet-Proof Shoes -




Author: balasa@cae.ca (Alexandru Balasa)
Date: Tue, 25 Oct 1994 12:29:35 GMT
Raw View
Ronald F. Guilmette (rfg@netcom.com) wrote:
: Here is a simple puzzle involving object destruction order.  What must
: the following program print (when compiled with a C++ compiler which
: claims to conform to the latest X3J16/WG21 draft)?

: --------------------------------------------------------------------------
: #include <stdio.h>
:
: struct T1 { ~T1 () { printf ("~T1\n"); } };
: struct T2 { ~T2 () { printf ("~T2\n"); } };
:
: struct A
: {
:     A () { static T1 T1_local; }
:     ~A () { printf ("~A\n"); }
: };
:
: struct B
: {
:     B () { static T2 T2_local; }
:     ~B () { printf ("~B\n"); }
: };
:
: A a;
: B b;
:
: int main () { return 0; }
: --------------------------------------------------------------------------

: I tried three different compilers (all on UNIX) and got three different
: set of results, i.e.:

:  Compiler 1 Compiler 2 Compiler 3
:  --------------------------------------------
:    ~T2    ~B    ~B
:    ~T1    ~A    ~A
:    ~B    ~T1    ~T2
:    ~A    ~T2    ~T1

: Which compiler is right?

Here is the 4-th result, from DEC cxx on OSF/1 1.3:
~B
~T2
~A
~T1
This makes sense to me; the inner object is destroyed after the outer
object ( otherwise, the B destructor would not have a whole object )
and B and A objects are destroyed in the reverse order; does it look
weird ?

Alexandru Balasa