28

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.

strager
  • 88,763
  • 26
  • 134
  • 176

7 Answers7

48

(1) the order of initialization of globals is not defined

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

The order of initialization of global variables in different translation units is unspecified.

(2) the order of initialization of globals ignores all dependencies

Right.

Is it possible to make sure the constructor of Foo is called before initializing dummy?

Yes, if globalFoo is defined before dummy and they are in the same translation unit.

One option would be to have a static pointer to the global instance; such a pointer will be initialized to null before any dynamic initialization takes place; addToGlobal can then test whether the pointer is null; if it is, then it is the first time the global is being used and addToGlobal can create the global Foo.

James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • By "Is it possible to make sure the constructor of Foo is called before initializing dummy?" I meant "other than changing the order of the definitions". Your answer makes things a bit clearer, but it doesn't really 'solve' my issue (yet). – strager Sep 19 '10 at 15:24
  • Seems to me like it would be preferable to have a static member of Foo which is the repository. Treating Foo as both a repository (i.e. globalFoo) and for other purposes does not 'feel right'. If on the other hand Foo 'has a' global repository then init should work OK and design seems cleaner. – Steve Townsend Sep 19 '10 at 15:27
  • 1
    I would rather have the singleton implemented as a function static: `Foo& global_foo() { static Foo foo; return foo; }` as there is a slightly better guarantee in the order of initialization: it will be initialized during the first call to the method. That will still have issues during finalization... – David Rodríguez - dribeas Sep 19 '10 at 15:50
  • @David - Finalization issues iff C++/CLI? – Steve Townsend Sep 19 '10 at 16:24
19

On the order of initialization, read the answer here.

On how to solve the initialization issue, you can push the global to be a static local variable in a function. There standard guarantees that the static local variable will be initialized in the first call to the function:

class Foo {
public:
   static Foo& singleton() {
      static Foo instance;
      return instance;
   }
};

Then your other global variables would access the variable as:

Foo::singleton().add();

Note that this is not generally considered as a good design, and also that even if this solves the initialization issues, it does not solve the order of finalization, so you should be careful not to access the singleton after it has been destroyed.

Community
  • 1
  • 1
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • I came across this solution after posting my question. I tried it, and it seems to work. However, there may be a problem: will there be multiple instances due to `static` inside of `singleton`? That is, could `Foo::singleton()` possibly return different things if it is declared and defined in a header file and included across various translation units? – strager Sep 19 '10 at 16:14
  • Another potential problem with this pattern is that AFAIK it's not safe to access singleton concurrently. – Just another metaprogrammer Sep 19 '10 at 16:32
  • 2
    @strager: When the local variable is declared `static` then there will be a single instance of it, even if the function is inlined (that is, compiled separatedly in different translation units) the linker must ensure that only a single definition of the variable exists. – David Rodríguez - dribeas Sep 19 '10 at 21:24
  • @FuleSnabel: During static initialization (the problem at hand) the application is still single threaded. If the `singleton` function is called by a single thread the first time, then the pattern is as thread safe as the object that it encapsulated. The problem with thread safety would arise if a second thread calls the function before the first call has completed initialization. If the object is initialized in a single thread this is thread safe. – David Rodríguez - dribeas Sep 19 '10 at 21:26
1

C++ lacks anything like Ada's pragma elaborate, so you can't make any assumptions whatsoever about the order initilizations will happen in. Sorry. It sucks, but that's the design.

T.E.D.
  • 44,016
  • 10
  • 73
  • 134
1

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

Herman Zu
  • 11
  • 1
0

You're correct, initialization of globals between translation units is undefined. It is possible to get around this using the singleton pattern. However, be warned that this design pattern is often misused. Also be warned that order or destruction of globals is also undefined, in case you have dependencies in the destructors.

rlbond
  • 65,341
  • 56
  • 178
  • 228
  • 1
    The order of destruction is actually well-defined: objects with static storage duration are destroyed in the reverse order of when their initialization or construction was completed (there are a few nuances, but that's the general rule). – James McNellis Sep 19 '10 at 15:32
0

How about having the static global variable be a pointer initialized to nullptr. Then before another global object tries to use the object, check for its creation and create if needed. This worked for me for creating a global registry of class creators where one could add new classes without changing the file that handled the registry. i.e.

class Factory {
   static map<string, Creator*>* theTable;
   static void register1(const string& string, Creator* creator);
...
};
...
map<string, Creator*>* Factory::theTable= nullptr;
void Factory::register1(const string& theName, Creator* creator) {   
    if (!theTable) theTable=new map<string, Creator*>;
    (*theTable)[theName]=creator;
}

This compiled and worked with VC++ in Visual Studio 2015.

I had tried using before this

class Factory {
    public:
      static map<string, Creator*>  theTable;
      static map<string, Creator*>& getTable();
      static void register1(const string& string, Creator* creator);
}
map<string, Creator*>  Factory::theTable;
map<string, Creator*>& Factory::getTable() {
   return theTable;
}
void Factory::register1(const string& theString, Creator* creator) {
   getTable()[theString]=creator; // fails if executed before theTable is created

}

but I still had exceptions thrown when theTable was not created before trying to insert an entry into the map, which can happen if the registration of the class is handled in a separate compilation unit from the factory logic.

0

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; 
};
shpc
  • 79
  • 1
  • 7