[comp.lang.c++] Summary of global constructor responses.

beshers@select.columbia.edu (Clifford Beshers) (10/25/88)

I got several responses to my query about the calling order of global
constructors.  Since it seems to be and important issue, I provide
a summary here.

The problem was, I referenced 'cout' in a global constructor and
the program dumped before the first line of 'main()', indicating
that my global constructor had been called before cout's.  I
compiled on a Sun4 with AT&T CC.

This problem does not occur on all compilers.  The patches seem
to be trivial for different versions of the AT&T compiler. Here
are the reasons why:

*************

From: patrick@cs.cornell.edu (Pat Stephenson)
Subject: Global constructors and C++ I/O streams;  problems and questions.

It was my interpretation of "the book" (sorry, reference not to hand) that all
library constructors should be called before any user code (including global
constructors).  So I ran across this problem too.  I was using cfront 1.2 and
the CC script distributed with it, under BSD.  If you're not, this will be
of no help to you.

CC finds the required global constructors by loading the user code and using a
program called "munch" on nm output to figure out what constructors need to be
called.  munch generates an extra bit of C code that calls the constructors
and destructors.  munch appears to assume that its input is sorted numerically,
which is not the default under BSD (it may be under sys5, I don't know).  So
just arrange for the nm output to be sorted numerically.  I just piped
the nm output through sort, ie replace the line:

	nm $NMFLAGS $OO | $munchC $P >__ctdt.c

with

	nm $NMFLAGS $OO | sort | $munchC $P >__ctdt.c

in CC.  I suppse there is an nm flag that would have the same effect.

This worked for me.

Pat

************
A similar solution follows:
************

From: tolerant!procase!ajs@ucbvax.berkeley.edu (Andrew Jay Schneider)
Subject: Static constructors...

You're correct, the order in which the static object's constructors
get called is important.  In the ``patch'' versions of the compiler
a hueristic is used to order the constructors so that objects
in libC.a get called ahead of those in the user's code.  In the
``munch'' versions I don't think this ordering takes place.

...

In this scheme, the constructor for the stream object would
be called ahead of the one in your code.  In fact, when
I compiled your example with a ``patch'' compiler it did
run correctly.  Using our older ``munch'' compiler broke
it.

...

In looking at the CC shell script I found that you can pass
parameters down to nm(1) via an environment variable.
Setting the variable NMFLAGS to -n (% setenv NMFLAGS -n) made
the program run correctly.

Andy Schneider
tolerant!procase!ajs@ucbvax.berkeley.edu (I think)

************

Some comments on what the compiler *should* be doing.
************

From: prl@iis.UUCP (Peter Lamb)
...

Although the streams and other libC classes should work properly,
you are quite right in believing that initialisation of static objects
whose correct initialisation depends on the initialisation of other static
objects is something that is *very* hokey in C++.

It _is_ very useful; the problem is that the Unix, and most other,
loaders are too dumb.

Some rules of thumb for doing this are (this assumes BSD4.[23].
I am not familiar enough with the finer points of the SysV ld and
loader utilities to know if all of these things will work there):

1)	Don't overuse the feature and try to avoid (where possible)
	static objects that depend on the initialisation of static
	objects in other files.

2)	Use explicit loads of object modules rather than libraries.
	You have more control over them. The BSD loader and munch
	together will ensure that the static constructors in the
	last-mentioned files (and libraries) will be executed before
	those in the earlier modules. Static destructors will be called
	for the object files left-to-right.

3)	If you need to put interdependent static objects in libraries
	use the old Unix v7 commands lorder and tsort to try to persuade
	the objects to be put into the library in the correct order:

	eg, for make:

	universal_class_lib.a: $(OBJECTS)
		rm -f universal_class_lib.a
		ar crv universal_class_lib.a `lorder $(OBJECTS) | tsort`
		ranlib universal_class_lib.a 	# This isn't needed for SysV

	If tsort doesn't complain about cycles in the data, the library
	will probably load in the correct order.

	If you can't get this to work, you will have to order the library
	by hand, or prehaps rearrange your objects in their files.

4)	The order of initialisation between object files is *not* defined
	as part of the language. So none of the above need necessarily
	work. Static constructors should be able to do IO through
	streams, and the mechanisms which allow this will probably
	mean that the other techniques will work as well.



It is a great pity that this can't be done *much* better, but Unix (and
most other) loaders are just too dumb (there are good reasons for loaders to
be dumb, too, unfortunately...).

-- 
Peter Lamb
uucp:  seismo!mcvax!ethz!prl	eunet: prl@ethz.uucp	Tel:   +411 256 5241
Institute for Integrated Systems
ETH-Zentrum, 8092 Zurich

************
A more pessimistic view.
************
From: bright@Data-IO.COM (Walter Bright)
Morals:
	1. Don't use stream I/O in constructors.
	2. Don't depend on the order in which module constructors are called.

Definition:
	Module constructor: the function inserted by the C++ compiler
	into each compilation unit (module). This function calls all
	the static constructors for that module, and also does any
	static initialization that cannot be done at compile-time.


************

Summary:

	The bug was caused by 'munch' getting unsorted input from
'nm' in the CC script.  The solutions were a) pipe the output of
nm through sort;  b) use a flag (-p or -n depending on your
system) to 'nm' to have it do the sorting; or c) use patch (a
later version of munch) which doesn't rely on sorted input.

	The compiler implementation seems responsible for coping with
the ordering of global constructor invocations;  the language
only specifies the ordering within a single module, not between
modules and libraries.

	The AT&T compiler chooses to order the invocations
back-to-front, i.e. the modules or libraries later in the command
line are called before those earlier.  (That is, with the above
patch applied.)

	Since the problem seems very system and loader dependent,
there is not formal specification in the language.  The best we
can expect is that all compilers document their ordering methods
clearly.

Cliff Beshers
Columbia University Computer Science Department
beshers@sylvester.cs.columbia.edu

jss@hector.UUCP (Jerry Schwarz) (10/25/88)

In article <5964@columbia.edu> beshers@select.UUCP (Jonathan M. Smith) writes:
>I got several responses to my query about the calling order of global
>constructors.  Since it seems to be and important issue, I provide
>a summary here.
>
>The problem was, I referenced 'cout' in a global constructor and
>the program dumped before the first line of 'main()', indicating
>that my global constructor had been called before cout's.  I
>compiled on a Sun4 with AT&T CC.
>

The situation is both more and less complicated than is suggested by
most of the prior responses in this group. The C++ language
definition says that the constructors for static objects are called
in the order they appear in a file,  but gives no assurance about
what order the initialization occurs between files.

The "fixes" provided manage to force particular orders in particular
cases in particular versions of particular compiling systems.  The
are special purpose and do not really solve the problem.

But a technique I have developed allows the programmer to achieve the
desired effect without making any implementation specific
assumptions. This technique is incorporated in the iostream package
that will probably be part of cfront release 2.0 and will allow you
to use iostream I/O from constructors with minimal care.  I will
sketch it here.  I more complete description is contained in a short
paper that I have written. (It hasn't been formally "released" yet,
but if you send me a note I'll send you a copy when it is available.)
Although it may seem complicated at first, once it is understood it
can be applied without much thought.

At the end of iostream.h there is a declaration of a class

	class Iostream_init {
	public:	static int init_count ;  // initialized to 0
		Iostream_init() ;
		~Iostream_init() ; 
		} ;
	static Iostream_init iostream_init ;

Thus the constructor for Iostream_init is called at startup once for
every file that includes iostream.h. Similarly, during exit
processing, the destructor is called once for every time the
constructor was called. The purpose of init_count is to ensure that
the real initialization and finalization actions occur only once.  In
particular cin and cout are initialized by the Iostream_init
constructor the first time it is called.   (A slight complication
arises because an iostream variable cannot be explicitly initialized.
An ugly kludge using the preprocessor lets me get around this.)

Because of its placement in the header file the Iostream_init
constructor will normally be called before any use of cout or cin. In
some cases explicit calls must be inserted.  My style calls for
initialization classes to be associated with almost all classes I
define.  Explicit calls to new Iostream_init must sometimes be added
in the constructors for initialization classes.  In particular, when
a constructor of class K does I/O, but the header associated with
class K  does not require iostream.h to be included before the
declaration of K, then a call to "new Iostream_init" must appear in
the constructor of K_init before the use of any K constructor.

Jerry Schwarz
AT&T Bell Labs, Murray Hill

prl@iis.UUCP (Peter Lamb) (10/27/88)

In article <5964@columbia.edu> beshers@select.UUCP (Jonathan M. Smith) writes:
>I got several responses to my query about the calling order of global
>constructors.  Since it seems to be and important issue, I provide
>a summary here.
...
>	The AT&T compiler chooses to order the invocations
>back-to-front, i.e. the modules or libraries later in the command
>line are called before those earlier.  (That is, with the above
>patch applied.)
>

	It may be a little counter-intuitive, but *DONT* go changing
	the order of the way in which munch generates it's table of
	constructor pointers.

	The constructors in libraries should get called *FIRST* (this is
	the problem that you were having in the first place - they weren't
	getting called first)

	Since libraries are usually somewhere down the righthand end
	of the load CC, their constructors need to be called before
	main's constructor. (This is assuming the nm command outputs
	the symbols for the earliest-loaded symbols first in its listing)

	Note: Mike Mobray, who usually gets such things right also posted
	a `fix' for munch to reverse the order of call of the static
	constructors. Don't do this in AT&T 1.2.1 C++. Munch does the
	right thing there.

	The right fix is to use nm -p (output the symbols in loader order)
	or nm -n (output the symbols in numerical order) in the CC script.

>	Since the problem seems very system and loader dependent,
>there is not formal specification in the language.  The best we
>can expect is that all compilers document their ordering methods
>clearly.

	True, and sadly this is typically not documented. Neither is the
	order in which constructors for member objects are called.
	(at least not in the first edition of the book)
>
>Cliff Beshers
>Columbia University Computer Science Department
>beshers@sylvester.cs.columbia.edu

-- 
Peter Lamb
uucp:  uunet!mcvax!ethz!prl	eunet: prl@ethz.uucp	Tel:   +411 256 5241
Integrated Ststems Laboratory
ETH-Zentrum, 8092 Zurich