[comp.lang.c++] Ordering of initialisation of static classes in *different* files ?

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_