Path: utzoo!utgpu!water!watmath!clyde!att!osu-cis!tut.cis.ohio-state.edu!rutgers!columbia!select.columbia.edu!beshers From: beshers@select.columbia.edu (Clifford Beshers) Newsgroups: comp.lang.c++ Subject: Summary of global constructor responses. Message-ID: <5964@columbia.edu> Date: 24 Oct 88 22:04:49 GMT Sender: news@columbia.edu Reply-To: beshers@select.UUCP (Jonathan M. Smith) Organization: Columbia University Department of Computer Science Lines: 184 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