robert@hslrswi.UUCP (J. Robert Ward) (01/15/88)
Hello C++ Gurus, It seems to me that there is a (small) defect in the definition of the C++ language. Namely, the order in which static objects are initialised is apparently undefined when more than one source file is under consideration. For example, consider the following file: # include <stream.h> struct foo { ostream *mystream ; // Internal value foo( ostream& s ) ; // Constructor with arg } ; foo::foo ( // The real constructor ostream& s ) { mystream = &s ; ... // Do something with the arg } foo foo1( cout ) ; // Use the constructor Question: how can I guarantee that cout is properly initialised by its constructor before it gets passed as an argument to the constructor foo::foo() ? Since both cout and foo1 are global objects, I presume the order of construction is undefined. Or am I missing something here ? The problem came about in trying to solve question 12 on page 167 of the C++ book. Can this problem really be solved assuming I am not allowed to change main() in any way ? Many thanks for any light shed on this problem, Cheers, - Robert. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ J. Robert Ward, ___________ Hasler AG, Belpstrasse 23, CH-3000 Berne 14, Switzerland | _ | | _| |_ | Tel.: +41 31 633922 | |_ _| | X.400: robert@hslrswi.hasler | |_| | Bitnet: robert%hslrswi.UUCP@cernvax.BITNET |_________| Uucp: ... {uunet,ukc,mcvax ... }!cernvax!hslrswi!robert ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
djones@megatest.UUCP (Dave Jones) (01/18/88)
in article <734@hslrswi.UUCP>, robert@hslrswi.UUCP (J. Robert Ward) says: > > Hello C++ Gurus, > > It seems to me that there is a (small) defect in the definition of > the C++ language. Namely, the order in which static objects are > initialised is apparently undefined when more than one source file > is under consideration. > ... > Question: how can I guarantee that cout is properly initialised > by its constructor before it gets passed as an argument to [a constructor for a global object]? Answer: I don't think you can. > The problem came about in trying to solve question 12 on page 167 > of the C++ book. Can this problem really be solved assuming I am > not allowed to change main() in any way ? If by "neatly" you mean "using cout", I don't think so. It seems unfortunate that cout requires a constructor to be called at startup time, but it does. The same is true of cerr. I expect this to be the source of lots of botched error-messages from constructors. You will not have such problems with fprintf() and write(). It's a little ironic that the requirement that a constructor be called is the cause of using uninitialized data, since constructors were intended to prevent just that from happening. On page 289, it says "Constructors for non-local static objects are called in the order they occur in a file...". I find nothing about ordering of constructor calls for objects declared in separate files. A little thought will convince you that it would be a non-trivial job to assure proper order of construction. In the case at hand, the object "cout" is declared in the C++ library. So where is its "occurance" in the PROGRAM? At the first spot at which it is declared "extern", I suppose. That is to say, at the first #include <stream.h>. But "munch", the program which builds the constructor-caller routine, has no knowlege of where that might be. So, in constructors, use fprintf() or write(), not cout and cerr. Or you can build your own ostream, if you just HAVE to: #include <stream.h> class sic_transit { public: sic_transit() { filebuf fileout(stdout); ostream myout(&fileout); myout << "I be.\n"; } ~sic_transit() { filebuf fileout(stdout); ostream myout(&fileout); myout << "I be history.\n"; } }; sic_transit gloria_mundi; main() { cout << "Hello, world.\n"; }
jss@hector.UUCP (Jerry Schwarz) (01/19/88)
In article <734@hslrswi.UUCP> robert@hslrswi.UUCP (J. Robert Ward) writes: > >It seems to me that there is a (small) defect in the definition of >the C++ language. Namely, the order in which static objects are >initialised is apparently undefined when more than one source file >is under consideration. > The technique I use is to avoid statically intialized objects of types with constructors in favor of a the following. In the header, myclass.h, that declares the objects I want initialized I do. static class Myclass_init { static int count ; public: Myclass_init() ; ~Myclass_init() ; } myclass_init ; In myclass.c I put the definitions: Myclass_init::Myclass_init() { if ( count++ > 0 ) return ; // Allocate and initialize whatever needs to be // initialized here } Myclass_init::~Myclass_init() { if ( --count > 0 ) return ; // Free (and destroy) whatever needs to be // freed here. } This means that typically I declare pointers to objects rather than the objects themselves. The constructor and destructor is called once for every time the header is included. The manipulations of "count" arrange that the initialization and freeing is done only once. Because the order of calls to static initializers within a file is guaranteed, and the declaration of myclass_init occurs higher in the file than any use of the objects declared in myclass.h, the initialzation occurs before any use and the freeing after any use. > >Question: how can I guarantee that cout is properly initialised >by its constructor before it gets passed as an argument to the >constructor foo::foo() ? Since both cout and foo1 are global >objects, I presume the order of construction is undefined. >Or am I missing something here ? > With regard to I/O, I wrote a replacement for streams that uses the above technique to initialize cin, cout and cerr. It will probably be included in the next release of cfront. Jerry Schwarz
robert@hslrswi.UUCP (J. Robert Ward) (01/19/88)
The story so far - here is my starting comment: >> It seems to me that there is a (small) defect in the definition of >> the C++ language. Namely, the order in which static objects are >> initialised is apparently undefined when more than one source file >> is under consideration. In article <218@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes: > >On page 289, it says "Constructors for non-local static objects are called >in the order they occur in a file...". I find nothing about ordering >of constructor calls for objects declared in separate files. A little >thought will convince you that it would be a non-trivial job to assure >proper order of construction. I agree that this is decidedly a "non-trivial job". However, it seems to me that guaranteed ordering of initialisation is required if one wants to write self-contained and independent modules that themselves use objects from other libraries. I think my example of cout not being properly initialised in good time illustrates this. As Dave Jones points out, it is possible to work around this problem *in this example*, but surely I shouldn't have to resort to such subterfuge. And besides, who says such work arounds are always available ? At the risk of being mercilessly flamed (no religious wars here, please!) I wish to point out that this problem is solved in Modula-2 so that modules relying on other objects being properly initialised can indeed do so (this is about the only good thing M2 has going for it, but that's a different story). Any further comments ? Cheers, - R. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ J. Robert Ward, ___________ Hasler AG, Belpstrasse 23, CH-3000 Berne 14, Switzerland | _ | | _| |_ | Tel.: +41 31 633922 | |_ _| | X.400: robert@hslrswi.hasler | |_| | Bitnet: robert%hslrswi.UUCP@cernvax.BITNET |_________| Uucp: ... {uunet,ukc,mcvax ... }!cernvax!hslrswi!robert ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
leech@tyler.cs.unc.edu (Jonathan Leech) (01/21/88)
Expires: Sender: Followup-To: Distribution: In article <735@hslrswi.UUCP> robert@hslrswi.UUCP (J. Robert Ward) writes: <regarding order of initialization of static objects> >>A little >>thought will convince you that it would be a non-trivial job to assure >>proper order of construction. >I agree that this is decidedly a "non-trivial job". If the ordering can be communicated to cfront, it can put keys into the per-file constructor & destructor tables (that is, into their symbol names). Then both the 'munch' and 'patch' approaches can sort the global table's contents by these keys. This would be tedious to code but not difficult. The hard part is then defining *what* the order of initialization should be, since dependencies are not so easily identified as with the IMPORT/EXPORT approach used in Modula-2 like languages. What is the ordering you propose? Jon Leech (leech@cs.unc.edu) __@/ ``After all, the best part of a holiday is perhaps not so much to be resting yourself as to see all the other fellows busy working.'' - Kenneth Grahame, _The Wind in the Willows_