[comp.lang.c++] Are global class objects special?

wilson@brillig.cs.umd.edu (09/27/90)

Am I right that there is no way to vary a parameter to 
a constructor for a class object having global scope 
without recompilation?   I would like to vary the seed 
to an object which is a random number generator used by 
several different objects (which is why it is global).  
Currently the seed must be hard coded since my tests show 
that the constructor is invoked before main is executed.  
I am using g++, the GNU C++ compiler.

For that matter, is there anything special about having a global 
class object?  I have code which runs properly when class objects
are declared within main, but when I move them up above main
the code crashes from within the constructor for the first object.
Actually the code crashes after trying to free up heap space used 
for constructing a char*  within the constructor.

I'm just generally puzzled about execution of code before main
in invoked.  Apparently, I can't count on all necessary 
system initializations being performed before my constructors
are called.

Does anyone have any insights to this issue?

Anne Wilson

jbuck@galileo.berkeley.edu (Joe Buck) (09/27/90)

In article <26729@mimsy.umd.edu>, wilson@brillig.cs.umd.edu writes:
> Am I right that there is no way to vary a parameter to 
> a constructor for a class object having global scope 
> without recompilation?

Well, since the constructor is invoked before main(), you
couldn't influence its arguments by means of command-line
parameters.  I suppose you could use environment variables
though:

class RNG {
public:
	RNG(int seed);
private:
	... whatever
}

RNG globalGenerator(atoi(getenv("SEED")));

which of course would blow up if SEED were not defined.  I don't
recommend this!

|> Currently the seed must be hard coded since my tests show 
|> that the constructor is invoked before main is executed.  

This is a feature of the language.

> For that matter, is there anything special about having a global 
> class object?  I have code which runs properly when class objects
> are declared within main, but when I move them up above main
> the code crashes from within the constructor for the first object.

This is probably an order-of-constructors problem.  For example,
if you try to do stream I/O from constructors with g++ and libg++
you'll get a crash when trying to construct a global object because
the constructor will be called before cin, cout, or cerr is
constructed.

> Actually the code crashes after trying to free up heap space used 
> for constructing a char*  within the constructor.

> I'm just generally puzzled about execution of code before main
> in invoked.  Apparently, I can't count on all necessary 
> system initializations being performed before my constructors
> are called.

This is a libg++ problem, though it can bite users of cfront in
some situations as well.  I've worked around the problem by
modifying gnulib3.c to call global constructors in
reverse order (so libraries will be initialized before your code).
You can also work around the problem by making sure your .o files
are handed to the linker in an order that works (constructors are
called in the order that the .o files are given to the linker).


--
Joe Buck
jbuck@galileo.berkeley.edu	 {uunet,ucbvax}!galileo.berkeley.edu!jbuck	

glenn@huxley.huxley.bitstream.com (Glenn P. Parker) (09/27/90)

In article <26729@mimsy.umd.edu> wilson@brillig.cs.umd.edu writes:
> Am I right that there is no way to vary a parameter to 
> a constructor for a class object having global scope 
> without recompilation?

I don't know about g++, but C++ allows you to initialize a global object
using a function call.  A function for a random seed might query the O.S.
for the time, or some such value.  Of course, you should not rely on
facilities like the streams library during this initialization, since they
require some initialization of their own.

An alternative (if the previous suggestion is not supported in g++) would
be to declare a global pointer to an object that is constructed in main().

--
Glenn P. Parker       glenn@bitstream.com       Bitstream, Inc.
                      uunet!huxley!glenn        215 First Street
                      BIX: parker               Cambridge, MA 02142-1270

steve@taumet.com (Stephen Clamage) (09/27/90)

wilson@brillig.cs.umd.edu writes:

>Am I right that there is no way to vary a parameter to 
>a constructor for a class object having global scope 
>without recompilation?   I would like to vary the seed 
>to an object which is a random number generator used by 
>several different objects (which is why it is global).  
>Currently the seed must be hard coded since my tests show 
>that the constructor is invoked before main is executed.  
>I am using g++, the GNU C++ compiler.

>For that matter, is there anything special about having a global 
>class object?  I have code which runs properly when class objects
>are declared within main, but when I move them up above main
>the code crashes from within the constructor for the first object.
>Actually the code crashes after trying to free up heap space used 
>for constructing a char*  within the constructor.

There is a slightly ugly but very portable technique to handle global
objects, which are slightly special.  It is used in the iostreams
library to get the standard streams initialized at the right time.

NOTE:  The word "static" has two meanings in C and C++: It means
having the lifetime of the program (non-auto), and it also means not
externally declared/defined (non-extern).  We need to use the word
in both meanings here.  I will qualify the word each time.

Within a compilation unit, static (non-auto) objects are created in the
order they are declared in the text, and destroyed in the reverse order.
There is no portable way to guarantee the order of initialization of
objects in different compilation units within a program.  (Another poster
mentioned that initialization occurs in the order in which the modules are
linked.  This may be true on some systems, but is by no means guaranteed.)

So we define an initializer class and a static (non-extern) instance of
the class in the class header which is included in every compilation
unit which needs to know about the class.  The class constructor
has a "one-time switch" to insure the body is executed only once.

file "myclass.h"
================
class myclass {
	....
};

class myclass_init {
	static int count;
public:
	myclass_init();
	~myclass_init();
};
static myclass_init mi;
================

file "myclass.c"
================
#include "myclass.h"

... // myclass member functions

myclass_init::count = 0;	// static member initialization

// this constructor will be executed before any other constructors in
// any modules which include "myclass.h"
myclass_init::myclass_init()
{
    if( ++count == 1 ) {
	// one-time processing here
    }
}

// this destructor will be executed after all other destructors in
// any modules which include "myclass.h"
myclass_init::~myclass_init()
{
    if( count-- == 1 ) {
	// one-time processing here
    }
}
================
================


A similar technique can be used to get exactly one object of a given
type statically constructed conceptually before main begins.


file "myclass.h"
================
class myclass {
	...
public:
	myclass();
};

class myclass_init {
public:
	myclass_init();
};
static myclass_init mi;
extern myclass *my;	// we want exactly one of these statically constructed
================

file "myclass.c"
================
#include <myclass.h>
...	// myclass member functions

myclass my = 0;	// defining instance of mi set to 0

myclass_init::myclass_init()
{
    if( ! my ) {
	my = new myclass();	// gets constructed exactly once
	...	// other processing
    }
}
================
================

As long as myclass.h is included in a file before any reference to
anything which depends on what it does, the static construction will
have occurred first.
-- 

Steve Clamage, TauMetric Corp, steve@taumet.com

roman@sparc17.hri.com (Roman Budzianowski) (09/28/90)

In article <28241@pasteur.Berkeley.EDU>, jbuck@galileo.berkeley.edu (Joe
Buck) writes:

> You can also work around the problem by making sure your .o files
> are handed to the linker in an order that works (constructors are
> called in the order that the .o files are given to the linker).
> 
> 
> --
> Joe Buck
> jbuck@galileo.berkeley.edu	 {uunet,ucbvax}!galileo.berkeley.edu!jbuck	


In cfront from SUN w/munch, the constructors are called in reverse
alphabetic order determined by file names.

hall@eclipse.stanford.edu (Keith Hall) (10/02/90)

In article <28241@pasteur.Berkeley.EDU> jbuck@galileo.berkeley.edu (Joe Buck) writes:
>
>In article <26729@mimsy.umd.edu>, wilson@brillig.cs.umd.edu writes:
>> I'm just generally puzzled about execution of code before main
>> in invoked.  Apparently, I can't count on all necessary 
>> system initializations being performed before my constructors
>> are called.
>
>This is a libg++ problem, though it can bite users of cfront in
>some situations as well.  I've worked around the problem by
>modifying gnulib3.c to call global constructors in
>reverse order (so libraries will be initialized before your code).
>You can also work around the problem by making sure your .o files
>are handed to the linker in an order that works (constructors are
>called in the order that the .o files are given to the linker).

Having the constructors called in the proper order seems so
fundamental that I don't understand why that behavior is not already
ensured by the compiler/linker.  Buck, can your mods to gnulib3
become a standard part of libg++?

Keith

ark@alice.att.com (Andrew Koenig) (10/03/90)

In article <1990Oct2.162451.3101@Neon.Stanford.EDU>, hall@eclipse.stanford.edu (Keith Hall) writes:

> Having the constructors called in the proper order seems so
> fundamental that I don't understand why that behavior is not already
> ensured by the compiler/linker.

What is the proper order?  How can a compiler determine what
that order is?
-- 
				--Andrew Koenig
				  ark@europa.att.com

andru@proton.lcs.mit.edu (Andrew Myers) (10/05/90)

In article <11427@alice.att.com> ark@alice.att.com (Andrew Koenig) writes:
>In article <1990Oct2.162451.3101@Neon.Stanford.EDU>, hall@eclipse.stanford.edu (Keith Hall) writes:
>
>> Having the constructors called in the proper order seems so
>> fundamental that I don't understand why that behavior is not already
>> ensured by the compiler/linker.
>
>What is the proper order?  How can a compiler determine what
>that order is?

With a small amount of work, it's possible to create a class which orders
global constructors (and other operations to be performed before main)
based on a dependency graph. Similarly, the same technique can be used to
unwind the process and do ordered global destruction, if desired.

Andrew