initialization order of function local static variables

Discussion in 'C++' started by Severin Ecker, Mar 9, 2010.

  1. 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
     
    Severin Ecker, Mar 9, 2010
    #1
    1. Advertising

  2. * 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
     
    Alf P. Steinbach, Mar 10, 2010
    #2
    1. Advertising

  3. Hi 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
     
    Severin Ecker, Mar 10, 2010
    #3
  4. * Severin Ecker:
    > Hi 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"


    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
     
    Alf P. Steinbach, Mar 10, 2010
    #4
  5. Hi,

    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
     
    Severin Ecker, Mar 10, 2010
    #5
  6. * Severin Ecker:
    > Hi,
    >
    > 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)."


    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
     
    Alf P. Steinbach, Mar 10, 2010
    #6
  7. On 10.03.2010 17:20, Alf P. Steinbach wrote:
    > * Severin Ecker:
    >> Hi,
    >>
    >> 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)."

    >
    > 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
     
    Severin Ecker, Mar 10, 2010
    #7
  8. * Severin Ecker:
    > On 10.03.2010 17:20, Alf P. Steinbach wrote:
    >> * Severin Ecker:
    >>> Hi,
    >>>
    >>> 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)."

    >>
    >> 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.


    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
     
    Alf P. Steinbach, Mar 10, 2010
    #8
  9. Severin Ecker

    James Kanze Guest

    On Mar 10, 7:20 am, "Alf P. Steinbach" <> wrote:
    > * Severin Ecker:
    > > 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.


    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
     
    James Kanze, Mar 11, 2010
    #9
  10. Severin Ecker

    James Kanze Guest

    On Mar 10, 11:38 pm, "Alf P. Steinbach" <> wrote:
    > * 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.


    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
     
    James Kanze, Mar 11, 2010
    #10
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Patrick Hoffmann
    Replies:
    3
    Views:
    2,854
    Christian Jan├čen
    Aug 8, 2003
  2. Paul Sheer
    Replies:
    2
    Views:
    445
    Paul Sheer
    May 15, 2004
  3. Martin Wells

    Static inline functions with static local variables

    Martin Wells, Oct 6, 2007, in forum: C Programming
    Replies:
    10
    Views:
    719
    Army1987
    Oct 8, 2007
  4. Sullivan WxPyQtKinter
    Replies:
    10
    Views:
    686
    Antoon Pardon
    Nov 8, 2007
  5. auspicious
    Replies:
    0
    Views:
    334
    auspicious
    Sep 25, 2008
Loading...

Share This Page