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
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
On 10.03.2010 08:20, Alf P. Steinbach wrote:
>> 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.
I think this also applies to constant initialization from what i read here.
>> 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.
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.
>> 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.
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
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).
>>> 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.
>
> 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
On 10.03.2010 10:48, Alf P. Steinbach wrote:
> 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
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...
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
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
Actually order is not important if they are PODS. If they
have constructors, then order would be order of calls.
Since most simplest way is to register at exit (you can't
actually predict across libraries),
order of destruction would be opposite of construction.
>
> 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?
Actually in my experience there is always problem with static
variables and dynamic libraries (you have to ensure that
startup code is called at all, watch out for linker). So if they are
in functions it worked always right. So Im really not
relaying on static members of classes that are part
of dynamic libraries, rather place statics in functions.
Greets
Sometimes online sometimes not
> > 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.
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.)
--
James Kanze
[...]
> >> 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.
I'm pretty sure that that's the intent, but it really could be
worded more clearly.
[...]
> >> 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)
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.
[...]
> > 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.
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.
--
James Kanze