C++ global initialization order ignores dependencies?

Viewed 19048

I think my problem is best described in code:

#include <stdio.h>

struct Foo;

extern Foo globalFoo;

struct Foo {
    Foo() {
        printf("Foo::Foo()\n");
    }

    void add() {
        printf("Foo::add()\n");
    }

    static int addToGlobal() {
        printf("Foo::addToGlobal() START\n");

        globalFoo.add();

        printf("Foo::addToGlobal() END\n");

        return 0;
    }
};

Foo globalFoo;

int dummy = Foo::addToGlobal();

int main() {
    printf("main()\n");

    return 0;
}

The above prints (with gcc 4.4.3):

Foo::Foo()
Foo::addToGlobal() START
Foo::add()
Foo::addToGlobal() END
main()

This is what I expect, and seems logical.

However, when I swap the following lines:

Foo globalFoo;
int dummy = Foo::addToGlobal();

into this:

int dummy = Foo::addToGlobal();
Foo globalFoo;

the program outputs the following:

Foo::addToGlobal() START
Foo::add()
Foo::addToGlobal() END
Foo::Foo()
main()

It seems instance methods of Foo are being called using an instance which has not yet been constructed! Something as simple as moving the declaration of a variable in the global scope is affecting the behaviour of the program, and that leads me to believe (1) the order of initialization of globals is not defined and (2) the order of initialization of globals ignores all dependencies. Is this correct? Is it possible to make sure the constructor of Foo is called before initializing dummy?

The problem I am trying to solve is filling a repository of items (a static instance of Foo) statically. In my current attempt, I am using a macro which (among other things) creates a global variable (in an anonymous namespace to avoid name clashing) whose initialization triggers the static initialization. Perhaps I'm tackling my problem from the wrong angle? Is there a better alternative(s)? Thanks.

7 Answers

The most reliable way to provide correct init order for globals...

1) Init order depends on object files order passed to linker. Straight or reverse -not matter. You may create test application to detect it.

2) Use appropriate utilities( nm for example ) to discover imports & exports for each object file that contains globals.

3) Build the dependencies graph, sort object files and build required order for correct linking. Resolve cycles manually if exists.

I use such procedure in my makefiles on Linux. It works...

Global variables in a single translation unit (source file) are initialized in the order in which they are defined.

It is important to add the note to this rule that the mere declaration does not define the order:

extern Foo globalFoo; // or just a ref that is defined at a single place
extern Foo & globalFooRef;

or as a static member

struct Global
{
    static Foo globalFoo; // or just a ref that is defined at a single place
    static Foo & globalFooRef; 
};
Related