initialization order of function local static variables

S

Severin Ecker

Hi,

I have a question regarding the initialization order/point in time of
function local objects with static storage duration (C++ standard
sections 6.7.4 and 3.6.2).

Please consider the following code snippet:

x.cpp

void x() {
static Foo foo;
}


y.cpp

void y() {
static Bar bar;
}


main.cpp
// assume proper inclusion of header files

int main() {
x();
y();

return 0;
}


Now, if Foo and Bar are PODs, reading the C++ standard section 6.7.4, an
implemenation is free to zero initialize foo and bar during the static
initialization process before the program flow enters main() and
therefore before either x() or y() are invoked? (and as such, reading
the rules from 3.6.2, the order whether foo or bar will be initialized
first is not specified.) Am I right thus far?

Now consider that Bar and Foo are classes with a non-trivial default
constructors (but constructors that are independent of the other class
as this would probably trigger one of the rules in 3.6.2), what's the
situation there? Will the local variables only be initialized (namely
the constructor be called) when the program flow reaches the local
variable definitions in x() and y() respectively or is the compiler
still allowed to perform early initialization?

Still the order of destruction must be the exact opposite of the order
of construction (even when early initialization is applied and therefore
"ignoring" which function was called first), yes?

One additional question to this: is the order of initialization of
objects with statid storage duration "implementation defined" or
"unspecified" (I guess, relying on the order itself yields undefined
behaviour, correct?)

Many thanks in advance!
best regards,
severin
 
A

Alf P. Steinbach

* Severin Ecker:
Hi,

I have a question regarding the initialization order/point in time of
function local objects with static storage duration (C++ standard
sections 6.7.4 and 3.6.2).

Please consider the following code snippet:

x.cpp

void x() {
static Foo foo;
}


y.cpp

void y() {
static Bar bar;
}


main.cpp
// assume proper inclusion of header files

int main() {
x();
y();

return 0;
}


Now, if Foo and Bar are PODs, reading the C++ standard section 6.7.4, an
implemenation is free to zero initialize foo and bar during the static
initialization process before the program flow enters main() and
therefore before either x() or y() are invoked? (and as such, reading
the rules from 3.6.2, the order whether foo or bar will be initialized
first is not specified.) Am I right thus far?

I think so, yes. But note that this is zero initialization. Which is a distinct
phase from dynamic initialization.

Now consider that Bar and Foo are classes with a non-trivial default
constructors (but constructors that are independent of the other class
as this would probably trigger one of the rules in 3.6.2), what's the
situation there? Will the local variables only be initialized (namely
the constructor be called) when the program flow reaches the local
variable definitions in x() and y() respectively
Yes.


or is the compiler
still allowed to perform early initialization?

It was never allowed to do early initialization in general. Static zero
initialization is a distinct phase from dynamic initialization like calling
constructors. The dynamic initialization is (in the case we're considering)
performed when the execution first passes through the declarations. This is the
basis of lazily constructed Meyers' singletons.

Still the order of destruction must be the exact opposite of the order
of construction (even when early initialization is applied and therefore
"ignoring" which function was called first), yes?
Yes.


One additional question to this: is the order of initialization of
objects with statid storage duration "implementation defined" or
"unspecified" (I guess, relying on the order itself yields undefined
behaviour, correct?)

I'm guessing that you're asking about the aspects that aren't well specified,
like the static initialization fiasco; if so, I don't know the exact wording,
because IMHO it would not be a good idea to rely on a given compiler's rules
even if they were well documented and defined.


Cheers & hth.,

- Alf
 
S

Severin Ecker

Hi Alf,

I think so, yes. But note that this is zero initialization. Which is a
distinct phase from dynamic initialization.

I think this also applies to constant initialization from what i read here.
It was never allowed to do early initialization in general. Static zero
initialization is a distinct phase from dynamic initialization like
calling constructors. The dynamic initialization is (in the case we're
considering) performed when the execution first passes through the
declarations. This is the basis of lazily constructed Meyers' singletons.

In that case I don't quite understand 3.6.2/2
"An implementation is permitted to perform the initialization of an
object of namespace scope with static storage duration as a static
initialization even if such initialization is not required to be done
statically, provided that
- the dynamic version of the initialization does not change the value
of any other object of namespace scope with static storage duration
prior to its initialization"

Does "dynamic version of the initialization" here refer to "being
initialized, once the program flow reaches said statement", or would
this also include a dynamic initializer like a constructor call.

I'm guessing that you're asking about the aspects that aren't well
specified, like the static initialization fiasco; if so, I don't know
the exact wording, because IMHO it would not be a good idea to rely on a
given compiler's rules even if they were well documented and defined.

Well in fact this all boils down to checking whether one of the
compilers (gcc) we're using is buggy in terms of order of destruction
for function local objects with static storage duration (which i doubt
personally), or if the compiler in fact is allowed to do early
initialization and then all bets are off on the order and we can't rely
on the objects being created in the order that the functions which
contain them are being called (msvc seems not to do any early
initialization in this case).

many thanks again and cheers,
severin
 
A

Alf P. Steinbach

* Severin Ecker:
Hi Alf,



I think this also applies to constant initialization from what i read here.


In that case I don't quite understand 3.6.2/2
"An implementation is permitted to perform the initialization of an
object of namespace scope with static storage duration as a static
initialization even if such initialization is not required to be done
statically, provided that
- the dynamic version of the initialization does not change the value
of any other object of namespace scope with static storage duration
prior to its initialization"

The key is "namespace scope".

In your example

void x() {
static Foo foo;
}

the object "foo" is not at namespace scope -- it's a local object of static
storage duration, as opposed to a namespace scope object of static storage
duration, which is why the wording above contains both phrases, "namespace
scope" and "static storage duration".


Does "dynamic version of the initialization" here refer to "being
initialized, once the program flow reaches said statement", or would
this also include a dynamic initializer like a constructor call.

At namespace scope it's not meaningful to talk about the program flow reaching
the statement.

But for a local object it is meaningful to talk about that (and the standard does).

Well in fact this all boils down to checking whether one of the
compilers (gcc) we're using is buggy in terms of order of destruction
for function local objects with static storage duration (which i doubt
personally), or if the compiler in fact is allowed to do early
initialization and then all bets are off on the order and we can't rely
on the objects being created in the order that the functions which
contain them are being called (msvc seems not to do any early
initialization in this case).

Hm, do you have a concrete example?

Anyway, if it is a real problem then I suggest looking in Andrei's "Modern C++
Design" -- or just the Loki library -- for singletons with destruction
policies and even resurrection-as-needed (a.k.a. Phoenix) functionality. ;-)

many thanks again and cheers,
severin

Cheers,

- Alf
 
S

Severin Ecker

Hi,

The key is "namespace scope".

hmm, not really, because 7.6.4 says: "An implementation is permitted to
perform early initialization of other local objects with static storage
duration under the same conditions that an implementation is permitted
to statically initialize an object with static storage duration in
namespace scope (3.6.2)."

so from what I understand here is, that also function local objects with
static storage duration can be early initialized (before main is
invoked) if the rules of 3.6.2 (col. speaking: the initialization
process must have no side effects and must result in the same state as
it would if the object were dynamically initialized) apply.
At namespace scope it's not meaningful to talk about the program flow
reaching the statement.

But for a local object it is meaningful to talk about that (and the
standard does).

you're right, i just referred to 3.6.2 'coming from' section 7.6.4.
Hm, do you have a concrete example?

I will see that i can boil down our real situation to a minimal
compiling sample, but in essence it boils down to teh question whether
function local objects with storage duration can be initialized before
main is invoked or only when program flow reaches the initialization
statement.
So.. in fact a dumbed down explanation of sections 7.6.4 and 3.6.2, so
that I understand it correctly and have profound argument of why we
shouldn't do... what we're doing.
Anyway, if it is a real problem then I suggest looking in Andrei's
"Modern C++ Design" -- or just the Loki library -- for singletons with
destruction policies and even resurrection-as-needed (a.k.a. Phoenix)
functionality. ;-)

HA, well if it only were that easy or even my call to make. I can only
try to point my finger at the standard and say 'don't do that because' :)

cheers,
severin
 
A

Alf P. Steinbach

* Severin Ecker:
Hi,



hmm, not really, because 7.6.4 says: "An implementation is permitted to
perform early initialization of other local objects with static storage
duration under the same conditions that an implementation is permitted
to statically initialize an object with static storage duration in
namespace scope (3.6.2)."

If you look at 3.6.2 you'll see that it's only permitting an optimization,
essentially catering to the possibility of having the object's value precomputed
and placed there by the OS program loader, nothing more.

3.6.3/1 then explicitly says that such early-initialization-optimization has no
effect on destruction order: "If an object is initialized statically, the object
is destroyed in the same order as if the object was dynamically initialized".

But of course, that doesn't mean the g++ always gets it right...


Cheers & hth.,

- Alf
 
S

Severin Ecker

* Severin Ecker:

If you look at 3.6.2 you'll see that it's only permitting an
optimization, essentially catering to the possibility of having the
object's value precomputed and placed there by the OS program loader,
nothing more.

3.6.3/1 then explicitly says that such early-initialization-optimization
has no effect on destruction order: "If an object is initialized
statically, the object is destroyed in the same order as if the object
was dynamically initialized".

You're right, I've totally read that one over.
But of course, that doesn't mean the g++ always gets it right...

In that case it really starts to smell like a possible compiler bug, but
I'll have to carefully check that one (although personally it gives me
the creeps when code totally depends on the exact order of such
initalization issues)

I hope I don't bug you too much, but I try to understand at least quite
a bit of all that stuff so i recognize such code landmines early enough
when i stumble over them.

So,.. there's one more thing that I just noticed when I re-read the
standard sections (it's just that it's been quite some time that I was
really used to reading and understanding the formal english used in
standards)

3.6.1-1 names only static initialization (= zero init. and init. with
constant expressions) and everything else which is dynamic initialization.
On the other hand 3.6.3-1 seems to make a distinction between a
constructor call and dynamic initialization ("These objects are
destroyed in the reverse order of the completion of their constructor or
of the completion of their dynamic initialization.") Am I being paranoid
now; but isn't the invocation of a constructor a form of dynamic
initialization and so the mention of a constructor in the sentence above
would not be needed without changing the meaning.

cheers,
Severin
 
A

Alf P. Steinbach

* Severin Ecker:
You're right, I've totally read that one over.


In that case it really starts to smell like a possible compiler bug, but
I'll have to carefully check that one (although personally it gives me
the creeps when code totally depends on the exact order of such
initalization issues)

I hope I don't bug you too much, but I try to understand at least quite
a bit of all that stuff so i recognize such code landmines early enough
when i stumble over them.

So,.. there's one more thing that I just noticed when I re-read the
standard sections (it's just that it's been quite some time that I was
really used to reading and understanding the formal english used in
standards)

3.6.1-1 names only static initialization (= zero init. and init. with
constant expressions) and everything else which is dynamic initialization.
On the other hand 3.6.3-1 seems to make a distinction between a
constructor call and dynamic initialization ("These objects are
destroyed in the reverse order of the completion of their constructor or
of the completion of their dynamic initialization.") Am I being paranoid
now; but isn't the invocation of a constructor a form of dynamic
initialization and so the mention of a constructor in the sentence above
would not be needed without changing the meaning.

Possibly Pete Becker or Andrew Koenig or James Kanze could answer that. I don't
know. I searched the active core language issues and found nothing.

Interestingly, in C++0x, although the whole paragraph has been reworked to deal
with threading, the wording "constructor or ... dynamic initialization" is
retained. If not for that I'd strongly suspect you're right. But as it is, I
think there's about a 50/50 chance that the wording is significant...

So I suggest posting this question to [comp.std.c++].


Cheers, & sorry, but I just can't imagine what it could be,

- Alf
 
J

James Kanze

* Severin Ecker:
I think so, yes. But note that this is zero initialization.
Which is a distinct phase from dynamic initialization.

The real question is: can you tell? It's guaranteed that the
objects will be zero initialized before anything else happens in
your program. It's also guaranteed that any static
initialization will occur after zero initialization, but before
anything else. But since the variable doesn't become visible
until you reach it in your code, how can you tell? (I imagine
that most compilers skip the zero initialization of variables
which have static initializers, since there's no way any user
code can possibly see the variable between zero initialization
and static initialization.)
 
J

James Kanze

* Severin Ecker:

[...]
I'm pretty sure that that's the intent, but it really could be
worded more clearly.

[...]
Historically, a lot of compilers got this wrong. (I even used
one which called the destructor of local statics whose
constructor had never been called, because program flow never
passed there.) Today... I'd like to say that I'd be a bit
surprised, but doing it correctly does mean interacting some
with the C library (atexit), and can be difficult, if not
impossible, in cases where the C++ library doesn't have access
to the internals of the C library (e.g. g++). In the case of
g++, I seem to recall some build option (or options) which
affected this, but I've forgotten them, and I don't have a
source handy to check what it was.

[...]
Possibly Pete Becker or Andrew Koenig or James Kanze could
answer that. I don't know. I searched the active core language
issues and found nothing.

Calling a user defined constructor is certainly dynamic
initialization, even if the constructor body is empty. I
suspect that the extra wording is just to ensure that there is
no misunderstanding; the issue is when an exception is raised in
things like:

void g()
{
static int x = func(); // func() throws
static MyClass c; // MyClass::MyClass() throws.
}

The variables are not considered initialized if an exception
occurs during their initialization, and a second call to g()
will try again.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top