[comp.std.c++] ARM on order of static initialization

bothner@sevenlayer.cs.wisc.edu (Per Bothner) (01/09/91)

I know the issue of the order of initialization of static
variables is an old one, but I cannot find an answer in the ARM.

Initialization of non-local statics is discussed in ARM 3.4.
Near the end of p.19 it says "No further order is imposed on
the initialization of objects from different translation
units." This (together with the 2nd sentence of para 2
on p. 20) implies that there *is* an order imposed on the
initialization of objects from the *same* translation unit.
But this order is nowhere defined, as far as I can tell.
(Certainly not in the last non-bullet para on p.19, where
it would belong.) Am I missing something, or is the ARM?
-- 
	--Per Bothner
bothner@cs.wisc.edu Computer Sciences Dept, U. of Wisconsin-Madison

wjw@otter.hpl.hp.com (William Wray) (01/10/91)

I couldn't find any references in the ARM about the order of
initialization of global static objects within a translation unit,
so I checked an ancient manuscript "The C++ Programming Language".
In TC++PL section 5.5.2 on Static Store, Stroustrup states that:

"Constructors for global static objects in a file are executed in
 the order in which the declarations occur; destructors are called
 in the reverse order.",

he then goes on to explain that static objects are useful for
the initialization of library objects, but doesn't explain how.

In the ARM section 3.4 commentary there is a fuller example of
the technique of using statics in library initialization,
a technique which clearly relies on the order of initialization.
The idea is to use a file-scope static object, defined in the
library header file, to initialize the library objects.  This
header will always be included before the library is used, so
the library objects should get initialized before they are used,
or so it says.....

The iostream library uses this technique to initialize cout, cin,
cerr and clog so that IO will be initialized before any use of IO.

The technique, however, is not foolproof. It is possible for users
to upset the order of initialization, intentionally or otherwise.

The following program demonstrates how a user's global object may
be initialized before the iostream objects are initialized.
The initialization of the user's object uses iostreams, and bombs.
To add a slight twist, the program defines a class `bomb` with a
global instance which is initialized using the nifty technique.

The program consists of 3 files: bomb.h bomb.C and main.C.

-------------------------bomb.h---------------------------------
class bomb {
public:
  void construct();
  void destruct();
};

extern bomb glob;

class bomb_init { 
  static int c;
public:
  bomb_init();
  ~bomb_init();
};

static bomb_init glob_init;

-------------------------bomb.C---------------------------------
#include <iostream.h>
#include "bomb.h"

bomb glob;

void bomb::construct() { cout << "bomb construct" << endl; }

void bomb::destruct() { cout << "bomb destruct" << endl; }

bomb_init::bomb_init() { if (!c++) glob.construct(); }

bomb_init::~bomb_init() { if (!--c) glob.destruct(); }

-------------------------main.C---------------------------------
#include "bomb.h"

int main() { }

----------------------------------------------------------------

On my system, when compiled with the command:
                           CC bomb.C main.C
the program executes as expected, but when the iostream library
(libC.a on my system) is explicitly linked in like this:
                           CC bomb.C /usr/lib/libC.a main.C
the program bombs.

The culprit is bomb.h which, when included in main.C, plants a
bomb - a static object with a constructor which calls a function
which calls iostreams which may not be initialized.  Whether
iostreams has been initialized depends on the order of initial-
ation of objects from different translation units, which is
implementation dependent. On my system, library units, such as
libC.a, are implicitly initialized first, so the program works.
That is, it works unless I force the main.C translation unit to
be initialized first, using the second compile command above.
  
In other words, this program has a portability problem.

The solution to the problem requires the programmer to include
iostream.h on the first line of bomb.h and, indeed, at the start
of any header file which contains static initializations which
call functions which call iostreams.

I'm sure that I'm missing something here.  What about static
initialization of objects without constructors?
I think that any C++ standard should be clearer on this subject
than the ARM.
                  -- Will Wray --

----------------ROBOTS OF THE UNIVERSE UNITE-------------------