Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

exceptions in ctors of static objects

10 views
Skip to first unread message

Martin Aupperle

unread,
Nov 18, 2001, 11:54:32 AM11/18/01
to
{strictly speaking this might be better asked from comp.std.c++
-mod/fwg}

Hello,

what must happen in a situation like

void f()
{
try
{
static A a;
}
catch( ... )
{
}

}

if A's ctor throws an exception, I expect that the exception will be
caught as usual . Can someone indicate to me the paragraph in the
Standard where this is described? I did not find anything in Chapter
15, which is about Exception Handling.

Thanks - Martin

------------------------------------------------
Martin Aupperle
MikeAlpha@NoSpam_csi.com
(remove NoSpam_)
------------------------------------------------

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

Francis Glassborow

unread,
Nov 18, 2001, 1:44:31 PM11/18/01
to
In article <3bf6cf3f....@News.CIS.DFN.De>, Martin Aupperle
<Martin....@quasar-gmbh.de> writes

>if A's ctor throws an exception, I expect that the exception will be
>caught as usual . Can someone indicate to me the paragraph in the
>Standard where this is described? I did not find anything in Chapter
>15, which is about Exception Handling.

See 6.7 paragraph 4.

--
Francis Glassborow
I offer my sympathy and prayers to all those who are suffering
as a result of the events of September 11 2001.

Carlos Moreno

unread,
Nov 20, 2001, 6:08:01 PM11/20/01
to

Martin Aupperle wrote:

>
> what must happen in a situation like
>
> void f()
> {
> try
> {
> static A a;
> }
> catch( ... )
> {
> }
>
> }
>
> if A's ctor throws an exception, I expect that the exception will be
> caught as usual.


I would too. But I'm not sure what the standard says about
this, and my language-lawyering skills are far from this
level :-)

I think the static object is not constructed until it's
used, which should be when you execute f() at runtime
(sure, it is static, and it will follow all the rules;
but while it hasn't been used, the compiler is not
breaking any rule by not constructing it, and I believe
compilers do exactly that -- though I'm not sure that
there's any guarantee from the standard)

Carlos

Francis Glassborow

unread,
Nov 21, 2001, 10:31:47 AM11/21/01
to
In article <3BF7F226...@m.com>, Carlos Moreno
<moreno_at_mo...@m.com> writes

>I think the static object is not constructed until it's
>used, which should be when you execute f() at runtime
>(sure, it is static, and it will follow all the rules;
>but while it hasn't been used, the compiler is not
>breaking any rule by not constructing it, and I believe
>compilers do exactly that -- though I'm not sure that
>there's any guarantee from the standard)

6.7 para 4 covers this, and the answer is yes it can do it early under
certain (not very restrictive) conditions provided by 3.6.2. I think
this means that it is necessary to be very careful about local statics
whose ctors can throw exceptions.


--
Francis Glassborow
I offer my sympathy and prayers to all those who are suffering
as a result of the events of September 11 2001.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Richard Smith

unread,
Nov 21, 2001, 6:18:35 PM11/21/01
to

"Carlos Moreno" <moreno_at_mo...@m.com> wrote in message
news:3BF7F226...@m.com...

[ ... ]

> I think the static object is not constructed until it's
> used, which should be when you execute f() at runtime
> (sure, it is static, and it will follow all the rules;
> but while it hasn't been used, the compiler is not
> breaking any rule by not constructing it, and I believe
> compilers do exactly that -- though I'm not sure that
> there's any guarantee from the standard)

I was about to reply "yes, there is, see 6.7/4", until I re-read that
paragraph. In fact, this paragraph explictly states that "an implementation
is permitted to perform early initialization of other local objects with
static storage under the same conditions that an implementation is permitted
to statically initialize an object with static storage duration in namespace
scope". I can't say I've ever encountered a compiler that takes advantage
of this sentence, though.

--
Richard Smith

Francis Glassborow

unread,
Nov 21, 2001, 9:15:37 PM11/21/01
to
In article <1006364936.20857....@news.demon.co.uk>,
Richard Smith <ric...@ex-parrot.com> writes

>I was about to reply "yes, there is, see 6.7/4", until I re-read that
>paragraph. In fact, this paragraph explictly states that "an implementation
>is permitted to perform early initialization of other local objects with
>static storage under the same conditions that an implementation is permitted
>to statically initialize an object with static storage duration in namespace
>scope". I can't say I've ever encountered a compiler that takes advantage
>of this sentence, though.

And I would much prefer that it was not allowed to do dynamic
initialisation early, not least because of implications on exception
safety.


--
Francis Glassborow
I offer my sympathy and prayers to all those who are suffering
as a result of the events of September 11 2001.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Nicola Musatti

unread,
Nov 22, 2001, 10:53:41 AM11/22/01
to

Francis Glassborow wrote:
[...]


> And I would much prefer that it was not allowed to do dynamic
> initialisation early, not least because of implications on exception
> safety.

I agree, even though the problem would still remain open for static
objects declared at file or namespace scope.

Cheers,
Nicola Musatti

Richard Smith

unread,
Nov 23, 2001, 12:07:24 AM11/23/01
to

"Francis Glassborow" <francis.g...@ntlworld.com> wrote in message
news:5A9EFjbL3D$7E...@robinton.ntlworld.com...

> In article <1006364936.20857....@news.demon.co.uk>,
> Richard Smith <ric...@ex-parrot.com> writes
> >I was about to reply "yes, there is, see 6.7/4", until I re-read that
> >paragraph. In fact, this paragraph explictly states that "an
implementation
> >is permitted to perform early initialization of other local objects with
> >static storage under the same conditions that an implementation is
permitted
> >to statically initialize an object with static storage duration in
namespace
> >scope". I can't say I've ever encountered a compiler that takes
advantage
> >of this sentence, though.
>
> And I would much prefer that it was not allowed to do dynamic
> initialisation early, not least because of implications on exception
> safety.

I hadn't thought about that aspect of it. With the following function, what
is the possible range of conformant behaviour?

#include <iostream>

int main()
{
try
{
;
}
catch (...)
{
std::cout << "This can't possibly happen. Can it?" << std::endl;
}

try
{
static char *tmp = new char[(int)1E9];
}
catch (...)
{
std::cout << "Fail" << std::endl;
return 1;
}

std::cout << "OK" << std::endl;
}

And what can happen? Well, there are two obvious possibilities:

Everything succeeds, tmp is initialised correctly at some point, we print
"OK" and exit with status 0.

New throws bad_alloc at the point of declaration of tmp, this gets caught,
we print "Fail", and exit with status 1.

And following the line of reasoning above: tmp might be early-initialised
before the first statement of main, new throws an exception which goes
uncaught, and the program aborts. It seems fairly clear that this behaviour
would be conformant.

Is it then legal to take this to its (il-)logical conclusion? tmp happens
to get early-initialised at the null statement at the start of main, new
throws bad_allow, this gets caught, the program prints "This can't possibly
happen. Can it?". I sincerely hope that this isn't legal, but I'm not
aware of a anything that stops an implementation from doing this (common
sense aside) and calling it conformant.

--
Richard Smith

Jeff Koftinoff

unread,
Nov 28, 2001, 8:04:46 AM11/28/01
to
In article <1006453480.27850....@news.demon.co.uk>,
"Richard Smith" <ric...@ex-parrot.com> wrote:


>
> I hadn't thought about that aspect of it. With the following function, what
> is the possible range of conformant behaviour?
>

<snip>


>
> Is it then legal to take this to its (il-)logical conclusion? tmp happens
> to get early-initialised at the null statement at the start of main, new
> throws bad_allow, this gets caught, the program prints "This can't possibly
> happen. Can it?". I sincerely hope that this isn't legal, but I'm not
> aware of a anything that stops an implementation from doing this (common
> sense aside) and calling it conformant.
>
> --
> Richard Smith
>

Wow, good point, Richard. Yes, I doubt that any implementation would
actually do that, but I CAN believe a similiar situation could happen in
real life:

....................
file a1.h:
....................
int f();
void g();

class A
{
public:
A(long x)
: ptr( new double[x] )
{
ptr[0]=3.14;
}

~A()
{
delete [] ptr;
}

double *ptr;
};

....................
file: a1.cpp:
....................
#include "a1.h"
int f()
{
return 10;
}

double g()
{
static A a((long)1e9);
return a.ptr[0];
}

....................
file a2.cpp:
....................
#include <iostream>
#include "a1.h"

int main()
{
int val=0;
try
{
val=f();
}
catch (...)
{
std::cout << "but f() can't throw anything!" << std::endl;
}

std::cout << "val is " << val << std::endl;

double fval=0.0;
try
{
fval = g();
}
catch( ... )
{
std::cout << "g() failed" << std::endl;
}
std::cout << "fval is " << fval << std::endl;

return 0;
}

.............
I believe that it is totally reasonable for an implementation to construct
the static object 'a' at the point f() is called - because they are in the
same translation unit. a1.o may be dynamically loaded (yes, all bets are
off in this case, dynamic linking isn't in the c++ standard, but it is the
real world - there are no c++ compilers available anymore that don't
support dynamic linkers)....

All the more reason to never ever use non-trivial static objects! Whenever
I have used static objects, I found in fact that it was a logic error on
my part - there should be a seperate class that holds this context
information because eventually you find your program needs multiple
contexts. Then, you can create multiple contexts and give them out to the
constructors of your other objects as required.

In addition, I know that threading is a null-issue in the C++ standard,
but in the real world I have encountered problems with static objects that
internally acquired a mutex. On some platforms, you can't acquire a mutex
until you have created a thread! And you have no chance to do that if your
static object is created before main() is called...

--jeff koftinoff <je...@jdkoftinoff.com>


--jeff

Richard Smith

unread,
Nov 30, 2001, 12:29:46 PM11/30/01
to

"Jeff Koftinoff" <je...@jdkoftinoff.com> wrote in message
news:jeffk-27110...@osx.147.168.192.in-addr.arpa...

> In article <1006453480.27850....@news.demon.co.uk>,
> "Richard Smith" <ric...@ex-parrot.com> wrote:
>
>
> >
> > I hadn't thought about that aspect of it. With the following function,
what
> > is the possible range of conformant behaviour?
> >
> <snip>
> >
> > Is it then legal to take this to its (il-)logical conclusion? tmp
happens
> > to get early-initialised at the null statement at the start of main,
new
> > throws bad_allow, this gets caught, the program prints "This can't
possibly
> > happen. Can it?". I sincerely hope that this isn't legal, but I'm not
> > aware of a anything that stops an implementation from doing this
(common
> > sense aside) and calling it conformant.
> >
> > --
> > Richard Smith
> >
>
> Wow, good point, Richard. Yes, I doubt that any implementation would
> actually do that, but I CAN believe a similiar situation could happen in
> real life:

[ example snipped ]

> I believe that it is totally reasonable for an implementation to construct
> the static object 'a' at the point f() is called - because they are in the
> same translation unit. a1.o may be dynamically loaded (yes, all bets are
> off in this case, dynamic linking isn't in the c++ standard, but it is the
> real world - there are no c++ compilers available anymore that don't
> support dynamic linkers)....

I agree it's would be reasonable for an implemenation to initialise 'a' when
f() is called, however, I can't say I've ever seen any implementations
prematurely instantiate local variables with static duration.

> All the more reason to never ever use non-trivial static objects! Whenever
> I have used static objects, I found in fact that it was a logic error on
> my part - there should be a seperate class that holds this context
> information because eventually you find your program needs multiple
> contexts.

Yes, I agree that it is usually a good idea to have a class to hold this
information, but what if it is only meaningful to have one instance of that
class in the program? This is when the Singleton design pattern is used,
and one very common implementation of this is,

class my_singleton
{
public:
static my_singleton &instance();

private:
singleton() { /* possibly throws an exception */ }
~singleton() {}
};

my_singleton &my_singleton::instance()
{
static my_singleton tmp;
return tmp;
}

And this uses a static variable whose constructor possibly throws an
exception, which opens it up to this problem. Yes, I agree that you can get
around this problem by only having a static pointer which is explicitly
initialised:

static my_singleton *tmp;
if (!tmp) tmp = new my_singleton;

However, this seems silly when there is a mechanism built into the language
to do exactly this, viz:

static my_singleton *tmp = new my_singleton;

... but this suffers from the potential problems with premature
initialisation.

> Then, you can create multiple contexts and give them out to the
> constructors of your other objects as required.

[ ... ]

I think that the main problem is that it is not explicitly stated anywhere
(to my knowledge) what should happen if the premature initialisation of
local object with static duration throws an exception.

15.1/2 implies that it is in principle possible to catch the exception using
the handler for "the compound-statement, ctor-initializer, or function-body
following the try keyword that was most recently entered by the thread of
control and not yet exited." But of course this could be practically
anywhere if the object is prematurely initialised. (At least, I think that
paragraph implies this -- I suppose it depends exactly what is meant by
"thread of control" -- a quick grep of the standard suggests that this is
the only place this phrase is used.)

However, 15.3/13 states "exceptions throw in destructors of objects with
static storage duration or in constructors of namespace-scope objects are
not caught by a function-try-block on main()." This suggests that if a
namespace-scope objects' constructor throws, even after the first statement
of main, then the namespace-scope contructor cannot be considered to be in
the same "thread of control" as the code that was being executed around
this. In this case, is it really intended that premature initialisation of
local statics is considered to be the same "thread of control"? This seems
rather inconsistant to me.

--
Richard Smith

0 new messages